Using simple array-based iteration not only ties algorithms to using arrays, but also requires that the
logic for determining which elements stay, which go, and in which order to process them, is known in
advance.
An iterator (also known as an enumerator) solves these problems by providing a generic interface for
looping over a set of data so that the underlying data structure or storage mechanism—such as an array,
database, and so on—is hidden
Whereas simple iteration generally requires you to write specific code
to handle where the data is sourced from or even what kind of ordering or preprocessing is required, an
iterator enables you to write simpler, more generic algorithms
Iterator Operations
public interface Iterator {
public void first();
public void last();
public boolean isDone();
public void next();
public void previous();
public Object current() throws IteratorOutOfBoundsException;
}
Not all data structures allow traversing the data in both directions, nor does it always make sense.
For this reason, it is acceptable for any of the traversal methods—first(), last(), next(), and previous()—to throw an UnsupportedOperationException to indicate this missing or unimplemented behavior
public class IteratorOutOfBoundsException extends RuntimeException {
}
The Iterable Interface
Iterable is an interface that provides a generic way to obtain an iterator from any data structure that supports it.
public interface Iterable {
public Iterator iterator();
}
Using an Iterator
Once an iterator has been obtained—
either by explicit construction or as an argument to a method—position to the beginning or end as
appropriate
iterator.first();
while (!iterator.isDone()) {
Object object = iterator.current();
...
iterator.next();
}
Iterator iterator = ...;
for (iterator.first();!iterator.isDone(); iterator.next()) {
Object object = iterator.current();
...
}
Standard Iterators
Array Iterator
The most obvious iterator implementation is one that wraps an array.
By encapsulating an array within an iterator, you can start writing applications that operate on arrays now and still extend easily to other data structures in the future.
With a reverse iterator, however, the same behavior can be achieved without re-sorting and without duplicated code.
When the application calls first(), the reverse iterator actually calls last() on the underlying iterator. When the application calls next(), the underlying iterator’s previous() method is invoked, and so on. In this way, the behavior of the iterator can be reversed without changing the client
code that displays the results, and without re-sorting the array
Filtering Iterator
One of the more interesting and useful advantages of using iterators is the capability to wrap or decorate
(see the Decorator pattern [Gamma, 1995]) another iterator to filter the return values
The filter iterator works by wrapping another iterator and only returning values that satisfy some condition,
known as a predicate
Each time the underlying iterator is called, the returned value is passed to the
predicate to determine whether it should be kept or discarded
public interface Predicate {
public boolean evaluate(Object object);
}
evaluate(), that is called for each value, and returning a Boolean to indicate whether the value meets the selection criteria or not