On this page
Iterator
The Iterator pattern provides a way to access elements of a collection sequentially without exposing its underlying representation.
Problem
Clients need to traverse different collection types uniformly without knowing internal structure.
Solution
interface Iterator<T> {
boolean hasNext();
T next();
}
interface Iterable<T> {
Iterator<T> iterator();
}
class BookShelf implements Iterable<String> {
private final List<String> books = new ArrayList<>();
void addBook(String title) { books.add(title); }
@Override
public Iterator<String> iterator() {
return books.iterator(); // delegates to ArrayList iterator
}
}
// Usage
BookShelf shelf = new BookShelf();
shelf.addBook("Clean Code");
shelf.addBook("Effective Java");
for (String book : shelf) { // uses iterator() internally
System.out.println(book);
}
Java Iterator Interface
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.isEmpty()) {
it.remove(); // safe removal during iteration
}
}
Iterable and Enhanced For-Loop
Any class implementing Iterable<T> works with enhanced for-loop:
public class Range implements Iterable<Integer> {
private final int start, end;
public Iterator<Integer> iterator() {
return new Iterator<>() {
int current = start;
public boolean hasNext() { return current <= end; }
public Integer next() { return current++; }
};
}
}
for (int i : new Range(1, 5)) {
System.out.println(i); // 1, 2, 3, 4, 5
}
Java Stream Iterator
Iterator<String> it = list.stream()
.filter(s -> !s.isEmpty())
.map(String::toUpperCase)
.iterator();
Custom Collection Iterator
class BinaryTree<T> implements Iterable<T> {
private Node<T> root;
public Iterator<T> iterator() {
return new InOrderIterator(root);
}
private class InOrderIterator implements Iterator<T> {
private final Deque<Node<T>> stack = new ArrayDeque<>();
InOrderIterator(Node<T> root) {
pushLeft(root);
}
public boolean hasNext() { return !stack.isEmpty(); }
public T next() {
Node<T> node = stack.pop();
pushLeft(node.right);
return node.data;
}
private void pushLeft(Node<T> node) {
while (node != null) {
stack.push(node);
node = node.left;
}
}
}
}
Fail-Fast vs Fail-Safe
| Type | Behavior | Example |
|---|---|---|
| Fail-fast | Throws ConcurrentModificationException |
ArrayList.iterator() |
| Fail-safe | Works on a snapshot | CopyOnWriteArrayList.iterator() |
When to Use
- Access collection elements without exposing internal structure
- Support multiple traversal methods (forward, backward, filtered)
- Provide a uniform interface for different collection types
Best Practices
- Implement
Iterableto support enhanced for-loops - Use
Iterator.remove()for safe removal during iteration - Prefer Stream API for functional traversal with filtering and mapping
- Return read-only iterators when the collection should not be modified