In a lot of situations it is better to handle data as streams to keep the memory profile low and to decrease latency. To access data in such an environment, it is often useful to use iterators which collect data out of a stream and group them into objects and let the application use the stream like a collection of objects.
There are situations where it is not easy to implement a Java iterator with keeping the contract. Imagine a situation where a complex XML file is coming in via an InputStream and only a part of the XML file is to be parsed and aggregated into a dedicated Object. In this case keeping the contract is not so easy, because keeping hasNext() and next() in sync is complicated…
For this situation, I use a simple design which is implemented in PureSol Technologies’ Streaming Library:
package com.puresoltechnologies.common.utils; import java.util.Iterator; import java.util.NoSuchElementException; /** * This is an abstract implementation of an iterator to be used as base class * for complex iterators to assure the {@link Iterator} contract. * * @author Rick-Rainer Ludwig * * @param <T> */ public abstract class AbstractIterator<T> implements Iterator<T> { private T next = null; /** * This method looks for the next entry to be returned. If no further entry * is found, <code>null</code> is returned. * * @return An object of T is returned representing the next entry to be * returned by the iterator. <code>null</code> is returned in case * no more entries are available. */ protected abstract T findNext(); @Override public final boolean hasNext() { if (next == null) { next = findNext(); } return next != null; } @Override public final T next() { if (!hasNext()) { throw new NoSuchElementException("No more elements in this iterator."); } T result = next; next = null; return result; } }
The main advantage is the fixed implementation of hasNext() and next(), which is not to be changed anymore. Additionally, the whole logic to check for the next enty and to created if present was moved into a single method, so that there is no mismatch anymore between hasNext() and next(),
Another advantage can be that, there is also now the possibility to peek for the next entry:
public abstract class AbstractIterator<T> implements Iterator<T> { /* * [...] */ public final T peek() { return next; } /* * [...] */ }
Or a little bit more restrictive:
public abstract class AbstractIterator<T> implements Iterator<T> { /* * [...] */ public final T peek() { if (!hasNext()) { throw new NoSuchElementException("No more elements in iterator.") } return next; } /* * [...] */ }