Conceptly
← All Concepts
πŸ”„

Iterator

BehavioralA pattern that traverses collection elements sequentially while hiding the internal structure

Iterator is a behavioral pattern that lets you access elements of a collection one by one in sequence without exposing the collection's internal structure. The client uses only a common interface like next() and hasNext(), without caring whether the data is stored in an array, a tree, or a hash map.

β–ΆArchitecture Diagram

πŸ” Structure

Dashed line animations indicate the flow direction of data or requests

Why do you need it?

Traversing a list is simple: just increment an index. But traversing nonlinear data structures like trees or graphs requires choosing between depth-first and breadth-first, deciding on filter conditions, and potentially varying the traversal method. Putting this traversal logic inside the collection class muddies its core responsibility, and every new traversal method means modifying the collection. If client code must know the collection's internals to traverse it, changing the data structure breaks every piece of code that traverses it.

Why did this approach emerge?

Collections were among the most common data structures from the earliest days of object-oriented programming. As collection types proliferated, the problem became clear: arrays, linked lists, trees, graphs, stacks, and queues each used different traversal approaches, and switching the data structure meant rewriting all traversal logic. GoF (Gang of Four) formalized Iterator because traversal changes as often as the data structure, yet the two were bundled together. Today, Java's Iterator, Python's __iter__/__next__, and JavaScript's Symbol.iterator all build iterators into the language as first-class features -- testament to how universal the need that this pattern addresses truly is.

How does it work inside?

Iterator is built on two axes. One is the collection (Aggregate) axis and the other is the iterator axis. The collection's only responsibility is storing and managing its elements. When traversal is needed, it produces an iterator object via a createIterator() method. The iterator acts as a cursor that remembers the current position. Calling next() returns the current element and advances the cursor; hasNext() checks whether the end has been reached. The key property is that cursor state lives inside the iterator. Creating multiple iterators on the same collection lets each traverse independently from its own position. If the underlying data structure changes from a tree to a graph, the client code still just calls next() and hasNext() and remains unaffected.

What is it often confused with?

Iterator and Composite both deal with complex structures but solve different problems. Composite focuses on recursively building a tree structure and treating it as a single object; Iterator focuses on separating how that structure is traversed. In practice, the combination of building a tree with Composite and traversing it with Iterator is common. Visitor is also compared. Both visit collection elements one by one, but Iterator focuses on the order and method of access, while Visitor focuses on what operation to apply to each element. Iterator is about 'in what order to retrieve,' while Visitor is about 'what to do after retrieval.'

When should you use it?

Iterators are already built into most modern languages, so directly implementing the pattern from scratch is rare. But there are still moments when understanding the pattern matters. Custom data structures are the classic case. For example, after representing an org chart as a tree, if you need both by-department traversal and by-rank traversal, attaching different iterators to the same tree is the natural design. Cursor-based traversal of database query results -- fetching one row at a time instead of loading everything into memory -- is also an Iterator variation. Wrapping a pagination API to present it as a continuous stream to the caller follows the same principle. There are cases where the pattern does not fit. When there is no need to access elements one at a time in sequence, or when the collection itself must be modified frequently during traversal, managing the iterator's cursor state adds complexity rather than reducing it.

Complex data-structure traversal -- exploring trees, graphs, and custom collections without exposing internalsUniform traversal interface -- traversing different collection types the same wayLazy evaluation -- fetching elements one at a time on demand to reduce memory usageMultiple traversal strategies -- applying depth-first, breadth-first, filtered, and other traversals to the same collection