The Iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It separates the traversal algorithm from the collection, letting multiple traversals proceed independently.

Intent and Motivation

Intent: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Motivation: A BookShelf might store books in an array, linked list, or B-tree. Client code that loops with for (int i = 0; i < shelf.size(); i++) is coupled to the array representation. Iterator exposes hasNext() and next() (or for...of) regardless of internal structure. Multiple iterators can traverse the same collection simultaneously (e.g., one reader comparing two positions). The collection does not need to know how it is traversed.

Iterator is so fundamental that most modern languages build it into the runtime (Iterable, Iterator, for...of, yield).

Structure (UML-like)

  ┌──────────────┐  createIterator()  ┌──────────────┐
│  Aggregate   │ ─────────────────► │   Iterator   │ (interface)
├──────────────┤                    ├──────────────┤
│ + createIter │                    │ + hasNext()  │
└──────▲───────┘                    │ + next()     │
       │                            │ + remove()   │ (optional)
       │ implements                 └──────▲───────┘
┌──────┴───────┐                           │ implements
│ConcreteAggr. │                    ┌──────┴───────┐
└──────────────┘                    │ConcreteIter. │
                                    ├──────────────┤
                                    │ - position   │
                                    │ - aggregate  │
                                    └──────────────┘
  

Participants:

  • Iterator — defines access and traversal interface.
  • ConcreteIterator — implements iterator; tracks current position.
  • Aggregate — defines factory method to create iterators.
  • ConcreteAggregate — implements the factory; returns a ConcreteIterator.

Java Example

  import java.util.*;

class Book {
    private final String title;
    Book(String title) { this.title = title; }
    public String toString() { return title; }
}

class BookShelf implements Iterable<Book> {
    private final List<Book> books = new ArrayList<>();

    void addBook(Book book) { books.add(book); }

    public Iterator<Book> iterator() {
        return books.iterator(); // uses Java's built-in iterator
    }
}

// Custom iterator example
class ReverseIterator implements Iterator<Book> {
    private final List<Book> books;
    private int position;

    ReverseIterator(List<Book> books) {
        this.books = books;
        this.position = books.size() - 1;
    }

    public boolean hasNext() { return position >= 0; }

    public Book next() {
        if (!hasNext()) throw new NoSuchElementException();
        return books.get(position--);
    }
}

// Usage
BookShelf shelf = new BookShelf();
shelf.addBook(new Book("Design Patterns"));
shelf.addBook(new Book("Clean Code"));

for (Book book : shelf) { // uses Iterator internally
    System.out.println(book);
}
  

JavaScript Example

  class Range {
  constructor(start, end, step = 1) {
    this.start = start;
    this.end = end;
    this.step = step;
  }

  // Make iterable — enables for...of
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    const step = this.step;

    return {
      next() {
        if (current <= end) {
          const value = current;
          current += step;
          return { value, done: false };
        }
        return { done: true };
      }
    };
  }
}

// Generator alternative
function* rangeGen(start, end) {
  for (let i = start; i <= end; i++) yield i;
}

for (const n of new Range(1, 5)) console.log(n);    // 1 2 3 4 5
for (const n of rangeGen(1, 5)) console.log(n);    // 1 2 3 4 5

// Built-in iterators
const arr = ['a', 'b', 'c'];
for (const [index, value] of arr.entries()) {
  console.log(index, value);
}
  

Real-World Use Cases

Framework / System Usage
Java Iterable / Iterator All standard collections (List, Set, Map) support enhanced for-loops.
Java Streams Spliterator extends Iterator for parallel traversal.
JavaScript for...of Works on any object implementing Symbol.iterator.
Python generators yield creates lazy iterators for memory-efficient traversal.
Database cursors JDBC ResultSet iterates rows without loading entire result into memory.
React reconciliation Fiber tree traversal uses iterator-like depth-first walk.

Pros and Cons

Pros Cons
Supports multiple simultaneous traversals of the same collection Simple iteration over arrays may not need a full iterator abstraction
Decouples traversal algorithm from collection structure Iterator invalidation — modifying collection during traversal causes errors
Hides internal representation from clients Custom iterators add boilerplate for simple collections
Uniform interface across different collection types remove() during iteration is tricky and often unsupported
Enables lazy evaluation and infinite sequences (generators) Performance overhead vs direct index access for array-backed collections

When to Use vs When NOT to Use

Use when:

  • You need to access collection contents without exposing internal structure.
  • You want to support multiple traversal methods (forward, reverse, filtered).
  • Traversal algorithms are complex or should be reusable across collections.
  • You need lazy, memory-efficient traversal of large or infinite datasets.

Do NOT use when:

  • The collection is a simple array and direct indexing is clear and sufficient.
  • You only need one standard forward traversal (built-in language iterators suffice).
  • Random access is the primary access pattern (use index-based access).
  • The collection is so small that abstraction adds no value.

Common Mistakes

  1. Modifying the collection during iteration — causes ConcurrentModificationException or skipped elements.
  2. Not implementing Iterable — in Java, forgetting Iterable prevents enhanced for-loops.
  3. Iterator not idempotent — calling next() without checking hasNext() throws exceptions.
  4. Exposing internal collection via iterator — returning the backing array/list allows external mutation.
  5. Reusing a single iterator for multiple traversals — create a new iterator per traversal.
  • Composite — iterators are commonly used to traverse composite tree structures.
  • Visitor — iterator traverses; visitor performs operations on each element.
  • Factory Method — aggregate’s createIterator() is a factory method.
  • Memento — iterator can capture traversal state for resuming later.
  • Strategy — different iterator implementations are strategies for traversal.