Understanding the Iterator Pattern in Java

The Iterator pattern is a widely-used design pattern that provides a way to access the elements of a collection object in sequential manner without exposing its underlying representation.

Java’s Collection Framework prominently uses this pattern, enabling easy traversal and operations on data structures.

What’s the Need?

Imagine having a collection, like an array or linked list. While these structures store data efficiently, the way to access their data might differ.

The Iterator pattern abstracts this access mechanism, allowing you to focus on processing the data rather than on how to fetch it.

Core Components

  1. Iterator: Interface defining the methods required for iteration (hasNext()next(), etc.)
  2. ConcreteIterator: Implementation of the Iterator interface.
  3. Aggregate: Interface defining methods to create and obtain iterators.
  4. ConcreteAggregate: Implementation of the Aggregate interface, usually representing the collection.

Java’s Built-in Support

Java already supports the Iterator pattern in its Collection Framework. Every collection has an iterator() method that returns an Iterator object, which can be used to traverse the collection.

For instance:

List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Implementing a Custom Iterator

Let’s create our own simple collection and iterator for demonstration:

Aggregate Interface

public interface Container {
    Iterator getIterator();
}

Iterator Interface

public interface CustomIterator {
    boolean hasNext();
    Object next();
}

Concrete Collection (e.g., a NameRepository)

public class NameRepository implements Container {
    private String names[] = {"Rob", "Julie", "Lora", "John"};

    @Override
    public Iterator getIterator() {
        return new NameIterator();
    }

    private class NameIterator implements CustomIterator {
        int index;

        @Override
        public boolean hasNext() {
            return index < names.length;
        }

        @Override
        public Object next() {
            if (this.hasNext()) {
                return names[index++];
            }
            return null;
        }
    }
}

Using the Iterator

public class IteratorPatternDemo {
    public static void main(String[] args) {
        NameRepository namesRepository = new NameRepository();

        Iterator iter = namesRepository.getIterator(); 
        while (iter.hasNext()) {
            String name = (String) iter.next();
            System.out.println("Name: " + name);
        }
    }
}

Advantages

  1. Single Responsibility Principle: It separates the responsibility of iteration from the actual collection.
  2. Flexibility: Easy to add different types of iterators or traversal strategies without changing the collection.
  3. Uniformity: Provides a uniform way to traverse different collections.

Conclusion

The Iterator pattern offers a clean and coherent way to traverse complex data structures. By abstracting the traversal mechanism, it promotes decoupling, readability, and flexibility in code.

Leave a Comment

Your email address will not be published. Required fields are marked *