Oct 22, 2018

The Iterator pattern

The Iterator pattern prescribes three roles: a collection, an iterator over the collection, and an object that uses the iterator to systematically visit all the items in the collection.

For example, print all the strings by using a loop with

for (int i = 0; i < someCollection.length; i ++)
   System.out.println(someCollection[i]);

Now we want to print out the longest string in the collection:

String longest = someCollection[0];
for (int i = 1; i < someCollection.length; i ++)
  if (someCollection[i].length() > longest.length())
   longest = someCollection[i];

Suppose we decide to use a different collection to store the strings, such as a Set or a List or whatever. All of the code would need to be modified to change the way it accesses the strings; further, the code would need to know the details of how the collection is stored.

The Iterator pattern separates the notion of “iterating over all the elements in the collection” from the details of how the collection is stored.

An iterator is always attached to some collection. Usually a collection has some method that creates an iterator for the collection. An iterator provides access to the elements of the collection in some order that is defined by the underlying collection.

If code is written to access all the elements of a collection through an iterator, and the kind of collection is changed, the only other code that may (or may not) need to be changed is the code that asks the collection to creates the iterator.

Java Iterator Example:

package ca.i88.ca.example.iterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

/**
 *
 * @author IT.I88.CA
 */
public class NewClass {

    public static void main(String args[]) {
        ArrayList list = new ArrayList();
        list.add("IT");
        list.add("I88");
        list.add("CA");
        for (Iterator it2 = list.iterator(); it2.hasNext();) {

            System.out.println("Next element: " + it2.next());

        }

        System.out.println("List iterator using while: ");
        ListIterator lit = list.listIterator();
        while (lit.hasNext()) {
            System.out.println("Next element: " + lit.next());
        }

        System.out.println("List iterator (backward iteration): ");
        lit = list.listIterator(list.size());
        while (lit.hasPrevious()) {
            System.out.println("Previous element: " + lit.previous());
        }
    }
}

Using iterator is the right way to delete elements inside loops. Consider the following code which removes elements during iteration:
ArrayList<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
for (int i = 0; i < list.size(); i++) {
 list.remove(i);
}
System.out.println(list);
The output is:
[b, d]
When an element is removed, the size of the list shrinks and the index changes. So if you want to delete multiple elements inside a loop by using the index, that will not work properly.

foreach loop in Java works like an iterator, but actually it is not. Consider the following code:

ArrayList<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
 
for (String s : list) {
 if (s.equals("a"))
  list.remove(s);
}

It will throw out ConcurrentModificationException. Instead the following is OK:

ArrayList<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
 String s = iter.next();
 
 if (s.equals("a")) {
  iter.remove();
 }
}

.next() must be called before .remove(). In the foreach loop, compiler will make the .next()called after the operation of removing element, which caused the ConcurrentModificationException.