Iterator
Think of a TV remote's "channel up" button. You don't need to know whether channels are
stored as a simple list, a satellite frequency table, or something more exotic underneath — you
just press the button and get the next one. The Iterator pattern gives code the
same ability over any collection: a consistent way to walk through elements
one at a time — hasNext() / next(), or a language's built-in iteration
protocol — without the calling code needing to know how the collection is actually stored.
⚠️ The Problem
Say you build a custom LinkedList class made of Node objects
chained together with .next pointers. Anywhere that needs to loop over it has to
know that internal detail: let node = list.head; while (node) { use(node.value); node =
node.next; }. Every single place in your codebase that iterates the list is now coupled
to "linked list made of nodes." If you later switch the internal storage to a plain array for
performance, every one of those loops breaks and has to be rewritten.
✅ The Solution
Give the collection a method that returns an Iterator object — something with a consistent
interface, like hasNext() and next(), or a language's built-in
iteration protocol. The iterator, not the calling code, knows how to walk the collection's
actual internal structure. Client code just calls the same two methods over and over
(while (it.hasNext()) { it.next(); }) regardless of whether the thing underneath
is a linked list, an array, or a tree — and if the internal structure changes later, only the
iterator itself needs to be updated.
Example 1 — a custom linked list with a built-in iterator
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
class LinkedList {
#head = null;
#tail = null;
add(value) {
const node = new Node(value);
if (!this.#head) {
this.#head = this.#tail = node;
} else {
this.#tail.next = node;
this.#tail = node;
}
return this;
}
// Implementing Symbol.iterator makes this class work in for...of,
// spread syntax, and anywhere else JavaScript expects an iterable.
[Symbol.iterator]() {
let current = this.#head;
return {
next() {
if (!current) return { value: undefined, done: true };
const value = current.value;
current = current.next;
return { value, done: false };
},
};
}
}
const list = new LinkedList().add("a").add("b").add("c");
for (const item of list) {
console.log(item); // "a", then "b", then "c"
}
console.log([...list]); // ["a", "b", "c"] — spread syntax works too
The for...of loop, and anything else expecting an iterable, has no idea
LinkedList is chained Node objects internally — it just calls
next() until done is true. If the internal storage
changed to a plain array tomorrow, every for...of list loop anywhere in the app
keeps working unchanged.
Example 2 — a secondary context: depth-first tree traversal
class TreeNode:
def __init__(self, value, children=None):
self.value = value
self.children = children or []
class DepthFirstIterator:
def __init__(self, root):
self._stack = [root]
def __iter__(self):
return self
def __next__(self):
if not self._stack:
raise StopIteration
node = self._stack.pop()
# Push children in reverse so the leftmost child is visited first.
self._stack.extend(reversed(node.children))
return node.value
class Tree:
def __init__(self, root):
self.root = root
def __iter__(self):
return DepthFirstIterator(self.root)
tree = Tree(TreeNode("root", [
TreeNode("A", [TreeNode("A1"), TreeNode("A2")]),
TreeNode("B"),
]))
for value in tree:
print(value) # root, A, A1, A2, B
Client code just writes for value in tree, exactly like it would for a list —
the fact that a stack-based depth-first walk is happening underneath is entirely hidden inside
DepthFirstIterator. Swapping to breadth-first traversal later means writing a
different iterator class; the for value in tree call site never changes.
👍 When To Use It
- You've built a custom collection type and want client code to loop over it without knowing its internal structure — arrays, linked lists, trees, graphs.
- You want to support multiple ways of traversing the same collection (depth-first vs breadth-first over a tree, forward vs reverse over a list) without changing the collection's public API.
- You want iteration to work uniformly with a language's existing loop syntax and library
functions (
for...of, spread,list(), list comprehensions) rather than a bespoke traversal method.
👎 When NOT To Use It
- When the collection is already a built-in type (array, list, dictionary) — it already has a standard iterator; writing your own would just be reinventing what the language gives you for free.
- When the whole collection is small and simple enough that exposing the raw internal structure directly causes no real coupling problem in practice.
- When you need random access by index far more often than sequential traversal — a plain indexable structure is a better fit than forcing everything through a step-by-step iterator.
❓ FAQ
Isn't this just... how for-loops already work in most languages?
In modern languages, yes — many bake the Iterator pattern directly into the language
itself: JavaScript's Symbol.iterator/for...of, Python's
__iter__/__next__, C#'s IEnumerable/IEnumerator,
and Java's Iterable/Iterator interfaces are all direct
implementations of this pattern baked into the standard library. So you're very often
using Iterator without ever writing one by hand — you only write a custom
iterator when you build your own collection type that isn't already one of the built-in
ones.
Can a collection support more than one kind of iterator?
Yes — nothing limits a collection to exactly one traversal order. A tree, for instance, can expose a depth-first iterator and a separate breadth-first iterator, or a method that takes a traversal strategy as a parameter. Client code picks whichever iterator fits the task without the collection's core data structure changing at all.
What happens if the collection changes while you're iterating over it?
This is a classic real-world gotcha, not something the pattern itself solves. Many languages either throw an error (Java, Python raise on concurrent modification) or produce undefined/surprising behavior if you mutate a collection mid-iteration. Iterators that need to tolerate concurrent changes typically snapshot the collection up front or use more careful locking — that's an implementation detail on top of the basic pattern, not part of its core definition.
🙋 There Are No Dumb Questions
Q: If my language already has for...of/foreach built in,
do I ever need to write a custom Iterator myself?
A: Only when you build your own collection type that isn't already a built-in array, list, or
dictionary — a custom linked list, tree, graph, or some domain-specific structure. For those,
implementing your language's iteration protocol is exactly what lets your custom type plug into
every existing loop, spread operator, and library function that already expects something
iterable.
✅ Quick Check
What is the main benefit of the Iterator pattern for code that loops over a collection?
Next up: what happens when a group of UI components all need to react to each other, and giving each one a direct reference to every other one turns into a tangled web of dependencies? That's the Mediator pattern.