BEHAVIORAL PATTERNS / OBJECT DESIGN BRIEF

Iterator pattern

The Iterator pattern provides a way to access elements of a collection sequentially without exposing its internal structure.

BeginnerPhase 06 / Topic 7 of 10ResponsibilitiesCollaborationsExtensibility
01

Overview

The Iterator pattern provides a way to access elements of a collection sequentially without exposing its internal structure. Whether the data is an array, a linked list, a tree, or a paginated API, clients use the same hasNext()/next() (or for...of) interface.

Iterators decouple traversal from storage, allow multiple traversal strategies (in-order, level-order, filtered), support lazy evaluation of large or infinite sequences, and let several traversals run at once. Most languages build them in: Java's Iterable, JavaScript's Symbol.iterator and generators, Python's __iter__.

A TV remote's channel button

You press 'next channel' without knowing how channels are stored or numbered internally. The remote remembers where you are and moves to the next one.

02

When to use it

  • Custom collections should work with for-each loops.
  • Different traversal orders over the same structure.
  • Lazy iteration over large or remote data (pagination, streams).
03

Where it shows up in interviews

Custom traversal

Recognize it when: iterate a tree, graph, or composite.

  • Design a file system iterator
  • BST iterator (LeetCode 173)
Lazy pagination

Recognize it when: iterate results from an API page by page.

  • Design an SDK list method
  • Design a playlist shuffle iterator
04

Where it is used in real software

Java Iterable and Streams

for-each loops and streams work on any Iterable.

JavaScript generators

function* and async iterators power lazy sequences and paginated SDKs (for await ... of).

Cloud SDK paginators

AWS SDK v3 paginators iterate across pages transparently.

05

Key terms

Iterator
Object tracking position with next()/hasNext().
Iterable / aggregate
Collection that creates iterators.
External vs internal iteration
Client drives next() vs collection calls a callback (forEach).
Fail-fast iterator
Throws if the collection changes during iteration.
Generator
Function that yields values lazily.
06

How it works, step by step

  1. 1
    Decide the traversal order

    In-order, BFS, filtered.

  2. 2
    Store traversal state in the iterator

    Stack, index, cursor.

  3. 3
    Implement next and hasNext

    Or a generator.

  4. 4
    Make the collection iterable

    Return a new iterator each time.

  5. 5
    Handle concurrent modification

    Fail fast or iterate a snapshot.

07

BST in-order iterator with a stack

Tree: 7 (3, 15 (9, 20))

Step 1 / 6
CallStack afterReturns
init[7, 3]-
next()[7]3
next()[15, 9]7
next()[15]9
next()[20]15
next()[]20

NOWCall: init | Stack after: [7, 3] | Returns: -

O(h) memory and amortized O(1) per next(), without flattening the tree.

08

Implementation

type TreeNode = { val: number; left?: TreeNode; right?: TreeNode }; class BST implements Iterable<number> {  constructor(private root?: TreeNode) {}   *[Symbol.iterator](): Iterator<number> {    const stack: TreeNode[] = [];    let node = this.root;    while (node || stack.length) {      while (node) { stack.push(node); node = node.left; }      node = stack.pop()!;      yield node.val;      node = node.right;    }  }} const tree = new BST({ val: 7, left: { val: 3 }, right: { val: 15, left: { val: 9 }, right: { val: 20 } } });console.log([...tree]); // [3, 7, 9, 15, 20] // Lazy pagination across an APIasync function* allOrders(fetchPage: (cursor?: string) => Promise<{ items: string[]; next?: string }>) {  let cursor: string | undefined;  do {    const page = await fetchPage(cursor);    yield* page.items;    cursor = page.next;  } while (cursor);}
09

Complexity and performance

BST iterator next()Amortized O(1)

O(h) memory.

Generator overheadSmall per yield

Lazy evaluation.

10

Trade-offs

Lazy vs eager

Lazy iteration saves memory but holds resources (cursors, connections) until finished.

Modification during iteration

Fail-fast iterators detect bugs; snapshot iterators are safe but cost a copy.

11

Variants and related techniques

Internal iteration

forEach(callback) where the collection drives the loop.

Cursor-based iteration

Database cursors and API page tokens.

12

Common mistakes

  • Modifying the collection in a for-each loop.

    Fix: Use iterator.remove() or collect changes first.

  • Reusing one iterator for multiple loops.

    Fix: Return a fresh iterator per call.

13

Interview questions

How would you iterate a BST in order with O(h) memory?

Keep a stack: push all left children from the root; next() pops a node, then pushes the left spine of its right child. Each node is pushed and popped once, giving amortized O(1) per call.

Why use an iterator instead of returning a list?

It hides the internal structure, supports lazy evaluation of large or infinite sequences, and allows different traversal orders without copying data.

14

Practice problems

ProblemDifficultyWhat it trains
Iterator over a nested list (flatten)MediumStack-based traversal.
Round-robin iterator over multiple listsMediumInterleaving.