Iterator Pattern
Iterator Pattern
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
Intent
The Iterator pattern separates the traversal algorithm from the collection it traverses. The collection (Aggregate) does not need to expose its internal structure; instead it produces an Iterator object that clients use to step through elements. Multiple iterators can traverse the same collection concurrently without interfering with each other. The GoF pattern named the protocol explicitly (hasNext() / next()); modern languages absorbed this into built-in language protocols — ECMAScript's iterator protocol (Symbol.iterator) and Java's Iterable<T> — so most Iterator usage today is implicit rather than explicit class-based.
When NOT to Use
- The collection is a simple array and
for...of,forEach, or stream operations cover all traversal needs — explicit Iterator adds nothing - The primary access pattern is random (index-based) rather than sequential — an iterator is the wrong abstraction
- The collection is never iterated externally (only consumed internally) — an iterator interface creates unnecessary surface area
When to Use
- Sequential traversal is needed over an internally complex structure (tree, graph, custom data structure) without exposing that structure to clients
- Multiple, independent traversal strategies are needed for the same collection (forward, reverse, filtered)
- A uniform interface is desired for iterating heterogeneous collections (Iterator as a protocol makes
for...ofwork on custom types)
How It Works
Participants: Aggregate (interface — declares createIterator() or [Symbol.iterator]()), ConcreteAggregate (implements the interface, returns a ConcreteIterator), Iterator (interface — next(), and historically hasNext() in GoF; IteratorResult<T> in ES2015), ConcreteIterator (tracks traversal position, implements iteration logic).
The key insight: iteration logic lives in the Iterator, not in the Aggregate. The Aggregate owns the data; the Iterator owns the traversal cursor. This allows the same collection to support multiple simultaneous traversals by producing independent Iterator instances.
GoF heritage vs native protocol: GoF formalised a named hasNext() / next() interface (p.257). ECMAScript 2015 (ES6) absorbed this into the built-in iterator protocol: any object implementing [Symbol.iterator]() that returns { next(): { value, done } } participates automatically in for...of, spread, destructuring, and Array.from. Java 5 introduced java.lang.Iterable<T> with iterator(), enabling the enhanced for-each loop — the same absorption of GoF Iterator into the language runtime.
Class Diagram
classDiagram
class Iterator {
<<interface>>
+hasNext() boolean
+next() T
}
class ConcreteIterator {
-collection : ConcreteAggregate
-currentIndex : int
+hasNext() boolean
+next() T
}
class Aggregate {
<<interface>>
+createIterator() Iterator
}
class ConcreteAggregate {
-items : T[]
+createIterator() Iterator
+getItem(index) T
+count() int
}
Iterator <|.. ConcreteIterator
Aggregate <|.. ConcreteAggregate
ConcreteAggregate --> ConcreteIterator : creates
ConcreteIterator --> ConcreteAggregate : traverses
TypeScript Example
// Iterator — TypeScript
// Source: GoF Design Patterns, p.257
// GoF heritage: hasNext()/next() absorbed into ECMAScript iterator protocol (ES2015)
class NumberRange {
constructor(private start: number, private end: number) {}
[Symbol.iterator](): Iterator<number> {
let current = this.start;
const end = this.end;
return {
next(): IteratorResult<number> {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined as unknown as number, done: true };
},
};
}
}
const range = new NumberRange(1, 5);
for (const n of range) {
console.log(n); // 1, 2, 3, 4, 5
}Java Example
// Iterator — Java
// Source: GoF Design Patterns, p.257
// GoF heritage: java.lang.Iterable<T> + java.util.Iterator<T> (Java 5+)
import java.util.Iterator;
class WordCollection implements Iterable<String> {
private final String[] words;
private int size = 0;
WordCollection(String... words) { this.words = words; this.size = words.length; }
@Override
public Iterator<String> iterator() {
return new Iterator<>() {
private int index = 0;
public boolean hasNext() { return index < size; }
public String next() { return words[index++]; }
};
}
}
// Client uses enhanced for-each — unaware of internal array representation
WordCollection wc = new WordCollection("alpha", "beta", "gamma");
for (String word : wc) {
System.out.println(word); // alpha, beta, gamma
}Related Concepts
| Pattern | Relationship |
|---|---|
| Composite-Pattern | Iterator is commonly used to traverse Composite trees (recursive structure iterates uniformly) |
| Factory-Method-Pattern | The Aggregate uses Factory Method to create the correct ConcreteIterator (decouples iterator creation from traversal) |
| Visitor-Pattern | Both visit elements; Iterator traverses structure, Visitor performs an operation on each element — orthogonal concerns |
Sources
- Gamma, Helm, Johnson, Vlissides — Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994, p.257
- ECMAScript 2015 Language Specification — Iterator Protocol (
Symbol.iterator)
Backlinks
Notes that link here: GoF-Patterns-MOC