Summary: The Iterator Pattern is demonstrated using Java source code examples.
The Iterator Design Pattern is one of the most simple and frequently used design patterns. The Iterator Pattern lets you sequentially move through a collection of objects using a standard interface, and without having to know the internal representation of that collection.
Put another way, the Iterator pattern defines an interface that declares methods for sequentially accessing the objections in a collection. A class that accesses a collection only through such an interface remains independent of the class that implements that interface. (Patterns in Java, Volume 1, Mark Grand.)
Motivation of the Iterator Pattern
The Iterator pattern has been a great improvement to the use of data collections in programming languages like Java. In the old days things like linked lists, arrays, vectors, and maps were all accessed through different APIs, but these days, because all these data collections can be traced back to common interfaces like Collection, List, and the use of the Iterator, programming with data collections is much easier.
The GoF Iterator Design Pattern
The original GoF Design Patterns book suggested an interface for the Iterator pattern that looks like this in Java:
public interface Iterator { public Object first(); public Object next(); public Object currentItem(); public boolean isDone(); }
These methods would do what they say, letting you move to first object in the collection; let you move to the next object; let you get the object the iterator is currently pointing to; and let you test to see if there are any more objects in the collection.
The Java Enumeration interface
In the early days the Iterator pattern in Java was partially implemented with an Enumeration interface that looks like this:
public interface Enumeration { public boolean hasMoreElements(); public Object nextElement(); }
As you can see, the Java Enumeration didn't match the Design Patterns Iterator pattern. However, I have to say that I don't recall ever missing a first() method, and the positive language of hasMoreElements() makes for a cleaner loop than the (negative) proposed isDone() method in the GoF Iterator pattern. That is, this loop:
while (hasMoreElements()) { // work done here }
is easier to read than this loop:
while (!isDone()) { // work done here }
The Java Iterator interface
In Java 1.2 the language included an Iterator interface, which looks like this:
public interface Interator { public boolean hasNext(); public Object next(); public void remove(); }
The addition of a remove() method was a nice addition, and other than the changes to the language, it's the only change between the Java Enumeration interface and the Java Iterator interface.
With its introduction into the Java API, the Iterator became available as a method in the Collection interface, and well, the rest is history. The Collection interface was implemented by many Java classes, including these:
- Set
- List
- Map
- SortedSet
- SortedMap
- HashSet
- many more ...
and with all of these classes implementing an Iterator method, the Iterator pattern in Java was well established.
Using the Iterator Pattern in Java
With the Iterator interface so well-ingrained in the Java API, using the Iterator Pattern in Java is very simple. You use it every time you iterate over any collection.
For example, (in the days before Java 5) imagine somewhere early in your code you had a List named people
that was defined like this:
List people = new ArrayList();
Later in your code your List is populated, and then when it comes to for you to access the elements in the List, you iterate over it, like this:
while (people.hasNext()) { Person person = (Person)people.next(); // do something with that person here ... }
When you write code like this, you are a consumer of the Iterator pattern in Java.
Using the Iterator Pattern in Java 5
With Generics in Java 5, the Iterator Pattern became even easier to use. First, we define the List a little differently:
List<Person> people = new ArrayList()<Person>;
And then later we access the List elements a little easier:
while (people.hasNext()) { // no need to cast the Object to Person Person person = people.next(); // do something with that person here ... }
The addition of Generics in Java 5 also made for loops easier to use and read:
for (Person person : people) { // do something with 'person' here ... }
Consequences of the Iterator Design Pattern
The primary consequence to consider about the Iterator Pattern in Java is what might happen is when you're iterating through a data collection, while that collection may be changed by another thread. (However, you'll have this "consequence" in any approach you take to accessing a collection of data in a multi-threaded environment.)
A second consequence is that its much easier to iterate over data collections than it was just 10-15 years ago. (If you were programming back then, you know what I mean; we all created our own LinkedList data structures in whatever language we used at that time.)
Finally, classes can provide iterators that iterate over their data collections in different ways. For instance, a collection of Person objects might have different iterators that return their data sorted by first name, last name, social security number, etc.
The Iterator Pattern in Java - Summary
I hope these Java Iterator Pattern examples have been helpful. As you've seen, the use of the Iterator Design Pattern in Java has made for a very clean programming language when it comes to dealing with all the data collections shown above: the Collections, List, and Iterator interfaces, and classes like List, Map, Set, and all of their sub-classes. The addition of Generics in Java 5 has made Java while and for loops even cleaner and easier to use.