Java > Design Patterns in Java > Behavioral Patterns > Iterator Pattern

Iterator Pattern Demonstration in Java

The Iterator Pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. This example showcases a simple implementation of the Iterator pattern using a collection of names.

Implementation

This code demonstrates the Iterator pattern with a `NameRepository` that holds a list of names. The `Aggregate` interface defines the `createIterator()` method, which is implemented by `NameRepository`. The `NameIterator` inner class implements the `Iterator` interface, providing the `hasNext()` and `next()` methods to traverse the names.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

interface Aggregate {
    Iterator<String> createIterator();
}

class NameRepository implements Aggregate {
    private final List<String> names = new ArrayList<>();

    public NameRepository(String... names) {
        this.names.addAll(List.of(names));
    }

    @Override
    public Iterator<String> createIterator() {
        return new NameIterator();
    }

    private class NameIterator implements Iterator<String> {
        int index;

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

        @Override
        public String next() {
            if (this.hasNext()) {
                return names.get(index++);
            }
            return null;
        }
    }
}

public class IteratorPatternDemo {
    public static void main(String[] args) {
        NameRepository nameRepository = new NameRepository("Robert", "John", "Julie", "Lora");

        for (Iterator<String> iter = nameRepository.createIterator(); iter.hasNext(); ) {
            String name = iter.next();
            System.out.println("Name : " + name);
        }
    }
}

Concepts Behind the Snippet

The key concepts are the `Aggregate` interface (representing the collection), the `Iterator` interface (defining traversal methods), and concrete implementations of both. This separation allows the client to traverse the collection without knowing its internal structure.

Real-Life Use Case

Iterators are heavily used in Java collections framework (e.g., `ArrayList`, `LinkedList`, `HashSet`). Whenever you use a `for-each` loop on a collection, you're implicitly using an iterator.

Best Practices

  • Favor using iterators over direct access to collection elements when you need to iterate through them.
  • Ensure the iterator's state is consistent with the underlying collection. Modifying the collection while iterating can lead to `ConcurrentModificationException`.

Interview Tip

Be prepared to explain the difference between an Iterator and an Enumeration. Also, be ready to discuss how the Iterator pattern promotes loose coupling and simplifies collection traversal.

When to Use Them

Use the Iterator pattern when:

  • You need to access an aggregate object's contents without exposing its internal representation.
  • You want to support multiple traversals of an aggregate object.
  • You want to provide a uniform interface for traversing different aggregate structures (i.e., polymorphic iteration).

Memory Footprint

The Iterator itself typically has a small memory footprint, usually consisting of an index or a reference to the current element. The memory consumption is primarily determined by the underlying collection being iterated over.

Alternatives

Alternatives to the Iterator pattern include:

  • Using direct indexing if the aggregate's internal structure is accessible.
  • Using streams (Java 8+) for more functional-style iteration and processing.

Pros

  • Single Responsibility Principle: Separates the traversal logic from the aggregate object.
  • Open/Closed Principle: Allows new traversal algorithms to be added without modifying the aggregate.
  • Supports varying iteration strategies

Cons

Can add complexity to the design, especially for simple collections where direct access might be sufficient. Requires maintaining iterator state, which can be tricky in concurrent scenarios.

FAQ

  • What happens if I modify the collection while iterating using the Iterator?

    Modifying the collection (adding or removing elements) while iterating using the Iterator can lead to a `ConcurrentModificationException`. To avoid this, use the iterator's `remove()` method if available, or use a thread-safe collection.
  • What is the difference between an Iterator and an Enumeration?

    The primary difference is that `Iterator` has `remove()` method (allows removing elements during iteration), while `Enumeration` does not. Also, `Iterator` method names are shorter (`hasNext()` vs. `hasMoreElements()`, `next()` vs. `nextElement()`).