Iterator
Iterator gives a uniform interface for sequentially traversing a collection without exposing its internals. It separates traversal from storage, supports multiple concurrent walks, and is built into most modern languages.
Iterator is a behavioral pattern that provides a standard interface to access the elements of an aggregate object one at a time, without revealing how the collection stores them. It separates the responsibility of traversal from the collection itself, so the same client code can walk arrays, trees, hash maps, or lazily generated sequences in the same way.
How It Works
The pattern defines an iterator interface, typically with hasNext and next, and a concrete iterator that tracks the current position within a specific collection. The aggregate exposes a factory method, often createIterator or iterator, that returns a fresh iterator bound to its contents. Because traversal state lives in the iterator rather than the collection, multiple iterators can traverse the same collection independently and simultaneously.
Most modern languages bake Iterator into the language: Java's Iterable, C#'s IEnumerable, Python's iterator protocol, and JavaScript's iteration protocol all formalize it. Lazy iterators compute elements on demand, enabling infinite or streamed sequences without materializing them in memory.
When to Use It
Use Iterator when you want to traverse a collection without exposing its internal structure, when you need several traversal strategies over the same data, or when you want a uniform traversal interface across different collection types. It is essential for libraries that must hide their storage choices while still letting callers loop over results, and for streaming data where elements are produced lazily.
Trade-offs
For a simple in-memory list, a built-in loop is often clearer than a hand-rolled iterator. Stateful iterators can break if the underlying collection is modified during traversal, which many implementations guard against with fail-fast checks that throw on concurrent modification. External iterators put control in the client, while internal iterators, such as forEach, control the loop themselves and limit early exit. Designing for both adds complexity.
Related Patterns
Composite structures are commonly traversed with an Iterator that flattens a tree into a sequence, whether depth-first or breadth-first. Visitor complements Iterator by applying an operation to each element the iterator yields. Null Object can represent an empty iterator so callers avoid null checks. Factory Method is what the aggregate uses to produce a matching iterator. Generators and coroutines, which suspend and resume to yield values on demand, are modern language-level expressions of the iterator idea, and reactive streams extend it with asynchronous, push-based delivery and backpressure.
Example
A social graph stores friends in an adjacency map. A FriendIterator exposes hasNext and next so a feed builder can loop over a user's friends without knowing they are stored in a map keyed by region. Swapping the storage to a database cursor later changes only the iterator implementation, not the feed code. The same abstraction lets a single piece of code page through a large result set, walk a paginated REST API one record at a time, or stream rows from a file, because each source hides its mechanics behind the same hasNext/next contract. A lazy iterator can even represent an unbounded sequence, such as successive lines from a live log, computing each element only when the consumer asks for it and never holding the whole sequence in memory.