Iterator Pattern
BeginnerProvides 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.
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 AliceTree 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.
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 AriaWhat is ConcurrentModificationException and how do you avoid it?
How would you implement an iterator for a binary tree that supports in-order traversal?
What is the difference between Iterator and ListIterator?
How does implementing Iterable enable use in for-each loops?
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.