Learnitweb

Iterator Design Pattern in Java

What is the Iterator Pattern?

The Iterator Design Pattern provides a way to access the elements of a collection sequentially without exposing the underlying representation (e.g., list, stack, tree, etc.).

It falls under the Behavioral Design Patterns category.

Provide a standard way to traverse through a collection of objects one at a time without exposing the internal structure of the collection.

Components of the Iterator Pattern

  • Iterator: Interface that defines the methods for traversing.
  • Concrete Iterator: Implements the Iterator interface.
  • Aggregate (Collection): Interface that defines a method to create an iterator.
  • Concrete Aggregate: A class that implements the Aggregate interface and returns an iterator.

Real-World Analogy

Think of a TV remote (iterator) that lets you go through TV channels (collection) without having to know how the TV stores the channel list internally.

Java’s Built-In Iterator

Java already provides the Iterator<E> interface in the java.util package with these methods:

boolean hasNext();
E next();
void remove(); // optional

But let’s learn how to implement the pattern from scratch to understand it deeply.

Custom Implementation of the Iterator Pattern

Step 1: Create the Iterator Interface

public interface MyIterator<T> {
    boolean hasNext();
    T next();
}

Step 2: Create the Aggregate Interface

public interface MyCollection<T> {
    MyIterator<T> createIterator();
}

Step 3: Create the Concrete Collection

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

public class NameCollection implements MyCollection<String> {

    private List<String> names = new ArrayList<>();

    public void addName(String name) {
        names.add(name);
    }

    public String getName(int index) {
        return names.get(index);
    }

    public int size() {
        return names.size();
    }

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

Step 4: Create the Concrete Iterator

public class NameIterator implements MyIterator<String> {

    private NameCollection collection;
    private int index = 0;

    public NameIterator(NameCollection collection) {
        this.collection = collection;
    }

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

    @Override
    public String next() {
        return collection.getName(index++);
    }
}

Step 5: Client Code (Test)

public class IteratorPatternDemo {

    public static void main(String[] args) {
        NameCollection nameCollection = new NameCollection();

        nameCollection.addName("Alice");
        nameCollection.addName("Bob");
        nameCollection.addName("Charlie");

        MyIterator<String> iterator = nameCollection.createIterator();

        System.out.println("Iterating over names:");
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Output

Iterating over names:
Alice
Bob
Charlie

Advantages

  • Encapsulation: The internal structure of the collection is hidden.
  • Uniform Interface: Different types of collections can be traversed uniformly.
  • Multiple Iterators: Multiple iterators can be created independently.

Limitations

  • Adds additional classes.
  • Not very useful for simple collections if you’re using Java’s built-in iterators.

When to Use

  • When you need to iterate over different types of collections in a consistent way.
  • When you want to decouple collection traversal logic from the collection itself.
  • When implementing custom collections.

Java’s Built-in Example (For Comparison)

import java.util.*;

public class JavaIteratorExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("One", "Two", "Three");

        Iterator<String> iterator = list.iterator();

        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}