16. Iterator
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
- Modifying the collection during iteration — causes
ConcurrentModificationExceptionor skipped elements. - Not implementing
Iterable— in Java, forgettingIterableprevents enhanced for-loops. - Iterator not idempotent — calling
next()without checkinghasNext()throws exceptions. - Exposing internal collection via iterator — returning the backing array/list allows external mutation.
- Reusing a single iterator for multiple traversals — create a new iterator per traversal.
Related Patterns
- 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.