diff options
| author | Ben Culkin <scorpress@gmail.com> | 2022-08-16 23:04:56 -0400 |
|---|---|---|
| committer | Ben Culkin <scorpress@gmail.com> | 2022-08-16 23:04:56 -0400 |
| commit | e43dc808d7304b90327c1def4452f6e3d9946983 (patch) | |
| tree | 7797eef414fb53d519076a471f8f8c65c61c71c5 | |
| parent | 081083574d93d983c0189907e5e829cb8c853188 (diff) | |
Update iterator capabilities
| -rw-r--r-- | src/main/java/bjc/data/MarkListIterator.java | 166 | ||||
| -rw-r--r-- | src/main/java/bjc/data/ResettableIterator.java | 14 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/Stack.java | 2 | ||||
| -rw-r--r-- | src/test/java/bjc/data/MarkListIteratorTest.java | 41 | ||||
| -rw-r--r-- | src/test/java/bjc/test/TestUtils.java | 8 |
5 files changed, 222 insertions, 9 deletions
diff --git a/src/main/java/bjc/data/MarkListIterator.java b/src/main/java/bjc/data/MarkListIterator.java new file mode 100644 index 0000000..725b050 --- /dev/null +++ b/src/main/java/bjc/data/MarkListIterator.java @@ -0,0 +1,166 @@ +package bjc.data; + +import java.util.*; + +/** + * ListIterator which allows navigation/marking of an iterator. + * + * @author bjcul + * + * @param <E> The element type + */ +public class MarkListIterator<E> implements ListIterator<E> { + private Iterator<E> backing; + + private List<E> cache; + private Deque<Integer> marks; + + private int currIdx; + private int maxIdx; + + /** + * Create a new marking list iterator. + * + * @param backing The iterator which backs us. + */ + public MarkListIterator(Iterator<E> backing) { + this.backing = backing; + + this.currIdx = 0; + this.maxIdx = 0; + + this.cache = new ArrayList<>(); + this.marks = new ArrayDeque<>(); + } + + /** + * Create a new marking list iterator. + * + * @param backing The iterable to get the backing iterator from. + */ + public MarkListIterator(Iterable<E> backing) { + this(backing.iterator()); + } + + @Override + public boolean hasNext() { + if (currIdx < maxIdx) + return true; + return backing.hasNext(); + } + + @Override + public E next() { + if (currIdx < maxIdx) { + return cache.get(currIdx++); + } + currIdx++; + maxIdx++; + E next = backing.next(); + cache.add(next); + return next; + } + + @Override + public boolean hasPrevious() { + return maxIdx > 0 && currIdx > 0; + } + + @Override + public E previous() { + currIdx--; + return cache.get(currIdx); + } + + @Override + public int nextIndex() { + return currIdx + 1; + } + + @Override + public int previousIndex() { + return currIdx - 1; + } + + /** + * Mark the current position in the iterator. + */ + public void mark() { + marks.push(currIdx); + } + + /** + * Reset the iterator to the last position that was marked, leaving the mark in + * place. + * + * @return Whether or not a rollback actually happened + */ + public boolean rollback() { + return rollback(false); + } + + /** + * Reset the iterator to the last position that was marked. + * + * @param clearMark Whether to clear the mark being rolled back to. + * + * @return Whether or not a rollback actually happened + */ + public boolean rollback(boolean clearMark) { + if (marks.isEmpty()) return false; + + if (clearMark) { + currIdx = marks.pop(); + } else { + currIdx = marks.peek(); + } + return true; + } + + /** + * Remove the last position that was marked. + * + * @return The marked position + */ + public int commit() { + return marks.pop(); + } + + /** + * Resets the cache state of this iterator. + * + * Once this has been called, all of the previous elements and marks are + * discarded. + */ + public void reset() { + this.cache = new ArrayList<>(); + this.marks = new ArrayDeque<>(); + + this.currIdx = 0; + this.maxIdx = 0; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Can't remove items"); + } + + @Override + public void set(E e) { + throw new UnsupportedOperationException("Can't set items"); + } + + @Override + public void add(E e) { + throw new UnsupportedOperationException("Can't add items"); + } + + /** + * Check if this iterator has at least one active mark. + * + * @return Whether this iterator has any active marks. + */ + public boolean hasMark() { + return !marks.isEmpty(); + } +} diff --git a/src/main/java/bjc/data/ResettableIterator.java b/src/main/java/bjc/data/ResettableIterator.java index 8b7f07a..8c1c4aa 100644 --- a/src/main/java/bjc/data/ResettableIterator.java +++ b/src/main/java/bjc/data/ResettableIterator.java @@ -5,12 +5,12 @@ import java.util.*; /* * @TODO Oct 6, 2020 - Ben Culkin - :CleverCache * - * In the future, there are certain efficencies we could take with our cached + * In the future, there are certain efficiencies we could take with our cached * elements; namely, the case where we repeat the same element multiple times, * or the case where we have a mixture of identical (and probably sizable) elements * - * The general downside to these of course, is that these efficencies would cost - * us something in terms of complexity, as well as not benefitting iterators which + * The general downside to these of course, is that these efficiencies would cost + * us something in terms of complexity, as well as not benefiting iterators which * aren't large enough. * * Still an interesting thought as to the best way to implement such a thing though. @@ -57,7 +57,7 @@ public class ResettableIterator<T> implements Iterator<T> { @Override public boolean hasNext() { if (isRepeating) return cacheIterator.hasNext() ? true : backing.hasNext(); - else return backing.hasNext(); + return backing.hasNext(); } @Override @@ -65,10 +65,10 @@ public class ResettableIterator<T> implements Iterator<T> { if (isRepeating) { if (cacheIterator.hasNext()) { return cacheIterator.next(); - } else { - cacheIterator = null; - isRepeating = false; } + + cacheIterator = null; + isRepeating = false; } T itm = backing.next(); diff --git a/src/main/java/bjc/esodata/Stack.java b/src/main/java/bjc/esodata/Stack.java index 360e57d..9dfee17 100644 --- a/src/main/java/bjc/esodata/Stack.java +++ b/src/main/java/bjc/esodata/Stack.java @@ -337,7 +337,7 @@ public abstract class Stack<T> { } /* - * Dataflow Combinators + * Data-flow Combinators */ /** diff --git a/src/test/java/bjc/data/MarkListIteratorTest.java b/src/test/java/bjc/data/MarkListIteratorTest.java new file mode 100644 index 0000000..6eb4994 --- /dev/null +++ b/src/test/java/bjc/data/MarkListIteratorTest.java @@ -0,0 +1,41 @@ +package bjc.data; + +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import bjc.test.TestUtils; + +/** + * Tests for {@link MarkListIterator} + * @author bjcul + * + */ +@SuppressWarnings("javadoc") +public class MarkListIteratorTest { + + @Test + public void test() { + List<String> list1 = Arrays.asList("a", "b", "c"); + + MarkListIterator<String> itr1 = new MarkListIterator<>(list1); + itr1.mark(); + + assertFalse(itr1.hasPrevious()); + assertTrue(itr1.hasNext()); + TestUtils.assertIteratorEquals(itr1, "a", "b", "c"); + + assertTrue(itr1.hasPrevious()); + assertFalse(itr1.hasNext()); + + itr1.rollback(false); + + assertFalse(itr1.hasPrevious()); + assertTrue(itr1.hasNext()); + TestUtils.assertIteratorEquals(itr1, "a", "b", "c"); + } + +} diff --git a/src/test/java/bjc/test/TestUtils.java b/src/test/java/bjc/test/TestUtils.java index d79157d..04cdf70 100644 --- a/src/test/java/bjc/test/TestUtils.java +++ b/src/test/java/bjc/test/TestUtils.java @@ -21,7 +21,13 @@ public class TestUtils { public static <T> void assertIteratorEquals(Iterator<T> src, T... vals) { for (int i = 0; i < vals.length; i++) { if (src.hasNext()) { - assertEquals(vals[i], src.next()); + T actNext = src.next(); + T expNext = vals[i]; + + String fmt = "mismatch at index %d"; + String msg = String.format(fmt, i); + + assertEquals(msg, expNext, actNext); } else { String msg = String.format("not enough values: got %d, wanted %d", i, vals.length); |
