Home/Learn/Low Level Design/Iterator Pattern

Iterator Pattern

Beginner
Behavioral Patterns

Provides a way to sequentially access elements of a collection without exposing its underlying representation.

Overview

The Iterator pattern decouples collection traversal from the collection's internal structure. The client uses a uniform hasNext()/next() interface regardless of whether the underlying structure is an array, linked list, tree, or graph. Java's java.util.Iterator and java.lang.Iterable are the canonical implementations — implementing Iterable lets a class be used in enhanced for-loops. Custom iterators are needed for non-standard traversals: reverse order, filtered access, lazy tree traversal. The pattern protects collection internals while offering controlled, sequential access.

Custom Iterator Implementation

Implement Iterable<T> on the collection and return an Iterator<T> from iterator(). The Iterator implements hasNext() and next(). This enables use in enhanced for-each loops and streams.

Java — Custom Iterator (IntRange + ReverseListIterator)
import java.util.Iterator;
import java.util.NoSuchElementException;

// Custom collection: a range of integers
public class IntRange implements Iterable<Integer> {
    private final int start;
    private final int end;   // exclusive

    public IntRange(int start, int end) {
        if (start > end) throw new IllegalArgumentException("start must be <= end");
        this.start = start;
        this.end   = end;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new RangeIterator();
    }

    // Inner class iterator — has access to start/end
    private class RangeIterator implements Iterator<Integer> {
        private int current = start;

        @Override
        public boolean hasNext() { return current < end; }

        @Override
        public Integer next() {
            if (!hasNext()) throw new NoSuchElementException();
            return current++;
        }
    }
}

// Usage — works in for-each
IntRange range = new IntRange(1, 6);
for (int n : range) {
    System.out.print(n + " "); // 1 2 3 4 5
}

// Reverse iterator for a list
public class ReverseListIterator<T> implements Iterator<T> {
    private final List<T> list;
    private int index;

    public ReverseListIterator(List<T> list) {
        this.list  = list;
        this.index = list.size() - 1;
    }

    @Override public boolean hasNext() { return index >= 0; }

    @Override
    public T next() {
        if (!hasNext()) throw new NoSuchElementException();
        return list.get(index--);
    }
}

List<String> names = List.of("Alice", "Bob", "Charlie");
Iterator<String> rev = new ReverseListIterator<>(names);
while (rev.hasNext()) System.out.print(rev.next() + " "); // Charlie Bob Alice

Tree Iterator (Level-Order)

Complex data structures need custom traversal strategies. A level-order (BFS) tree iterator uses an internal queue — the client calls hasNext()/next() without knowing about the queue.

Java — Level-order tree iterator with Queue
public class BinaryTree<T> implements Iterable<T> {

    public static class Node<T> {
        T value;
        Node<T> left, right;
        Node(T value) { this.value = value; }
    }

    private Node<T> root;
    public BinaryTree(Node<T> root) { this.root = root; }

    @Override
    public Iterator<T> iterator() {
        return new LevelOrderIterator();
    }

    private class LevelOrderIterator implements Iterator<T> {
        private final java.util.Queue<Node<T>> queue = new java.util.LinkedList<>();

        LevelOrderIterator() {
            if (root != null) queue.offer(root);
        }

        @Override public boolean hasNext() { return !queue.isEmpty(); }

        @Override
        public T next() {
            if (!hasNext()) throw new NoSuchElementException();
            Node<T> node = queue.poll();
            if (node.left  != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
            return node.value;
        }
    }
}

// Client code is identical regardless of traversal order
BinaryTree<Integer> tree = buildTree();
for (int val : tree) {
    System.out.print(val + " "); // BFS level-order output
}

Key Points to Remember

  • 1Implementing Iterable<T> allows use in enhanced for-each and java.util.stream.StreamSupport.
  • 2Always throw NoSuchElementException (not null) when next() is called beyond the last element.
  • 3Iterator state is per-iterator — multiple iterators can traverse the same collection concurrently.
  • 4ConcurrentModificationException is thrown when a collection is modified during iteration; use CopyOnWriteArrayList or ListIterator.remove() instead.
  • 5Java Streams are lazy iterators that compose operations without materializing intermediate collections.

Interview Questions

Sign in to ask Aria
1

What is ConcurrentModificationException and how do you avoid it?

MediumAmazon
2

How would you implement an iterator for a binary tree that supports in-order traversal?

HardGoogle
3

What is the difference between Iterator and ListIterator?

EasyMicrosoft
4

How does implementing Iterable enable use in for-each loops?

EasyFlipkart

Ask Aria about Iterator Pattern

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…