← Back to All Design Patterns

Iterator Pattern

🎯 Explain Like I'm 5...

Imagine you have a TOY TRAIN with many different cars attached together! 🚂

😟 The Problem:

  • You want to visit each train car (engine, passenger car, cargo car, caboose)
  • But you don't know HOW the cars are connected (hooks? magnets? clips?)
  • You just want to see what's in each car, one by one! 🤔

💡 The Solution - Use an Iterator!

  • The conductor gives you a special ticket! 🎫
  • The ticket says 'NEXT CAR' on it
  • Each time you use the ticket, you move to the next car
  • You don't need to know about hooks or magnets - just say 'NEXT'! 🎉

🌟 The Key Idea:

An iterator lets you visit each item in a collection (like train cars) ONE BY ONE, without knowing how they're stored or connected! It's like having a remote control that just has a 'NEXT' button! 🎮

🚀 Why Is This Pattern Useful?

  • Access elements without exposing the internal structure!
  • Use the same way to go through different collections!
  • Have multiple iterators on the same collection at once!

📋 Pattern Purpose

The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It separates the traversal logic from the collection itself.

⚡ When to Use Iterator Pattern

  • You want to access a collection's contents without exposing its internal structure
  • You need to support multiple simultaneous traversals of the same collection
  • You want to provide a uniform interface for traversing different collection types
  • You need different ways to traverse the same collection (forward, backward, filtered, etc.)

🔧 Iterator Pattern Components

Iterator Interface:

Defines methods for accessing and traversing elements (hasNext(), next())

Concrete Iterator:

Implements the iterator interface and tracks the current position

Aggregate Interface:

Defines an interface for creating an iterator

Concrete Aggregate:

Implements the aggregate interface and returns a concrete iterator

💻 Java Implementations

Example 1: Book Collection Iterator

Creating a custom iterator for a book collection (BookShelf with BookIterator).

Iterator.java
java
1
2
3
4
5
// Iterator Interface - defines traversal methods
public interface Iterator<T> {
boolean hasNext();
T next();
}
Container.java
java
1
2
3
4
// Aggregate Interface - defines method to create iterator
public interface Container<T> {
Iterator<T> createIterator();
}
Book.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// The element we're storing
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
@Override
public String toString() {
return """ + title + "" by " + author;
}
}
BookShelf.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Concrete Aggregate - the collection
public class BookShelf implements Container<Book> {
private Book[] books;
private int index = 0;
public BookShelf(int size) {
books = new Book[size];
}
public void addBook(Book book) {
if (index < books.length) {
books[index] = book;
index++;
}
}
public Book getBookAt(int i) {
return books[i];
}
public int getLength() {
return index;
}
@Override
public Iterator<Book> createIterator() {
return new BookIterator(this);
}
}
BookIterator.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Concrete Iterator - implements traversal
public class BookIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int currentIndex;
public BookIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.currentIndex = 0;
}
@Override
public boolean hasNext() {
return currentIndex < bookShelf.getLength();
}
@Override
public Book next() {
if (!hasNext()) {
throw new IndexOutOfBoundsException("No more books");
}
Book book = bookShelf.getBookAt(currentIndex);
currentIndex++;
return book;
}
}
BookShelfDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Client code - uses the iterator
public class BookShelfDemo {
public static void main(String[] args) {
BookShelf shelf = new BookShelf(5);
shelf.addBook(new Book("Design Patterns", "Gang of Four"));
shelf.addBook(new Book("Clean Code", "Robert Martin"));
shelf.addBook(new Book("Effective Java", "Joshua Bloch"));
shelf.addBook(new Book("Head First Java", "Kathy Sierra"));
System.out.println("Books on the shelf:");
Iterator<Book> iterator = shelf.createIterator();
// We don't need to know HOW books are stored!
while (iterator.hasNext()) {
Book book = iterator.next();
System.out.println("- " + book);
}
System.out.println("\nIterating again (creates new iterator):");
Iterator<Book> iterator2 = shelf.createIterator();
while (iterator2.hasNext()) {
System.out.println("- " + iterator2.next().getTitle());
}
}
}

Example 2: Name Repository Iterator

Implementing an iterator for a name repository with custom traversal.

NameRepository.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Concrete Aggregate with internal storage
public class NameRepository implements Container<String> {
private String[] names = {"Alice", "Bob", "Charlie", "David", "Emma"};
@Override
public Iterator<String> createIterator() {
return new NameIterator();
}
// Inner class - Concrete Iterator
private class NameIterator implements Iterator<String> {
private int index = 0;
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public String next() {
if (!hasNext()) {
throw new IndexOutOfBoundsException("No more names");
}
return names[index++];
}
}
}
NameRepositoryDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Client code
public class NameRepositoryDemo {
public static void main(String[] args) {
NameRepository repository = new NameRepository();
System.out.println("Names in repository:");
for (Iterator<String> iter = repository.createIterator();
iter.hasNext(); ) {
String name = iter.next();
System.out.println("Name: " + name);
}
// Multiple simultaneous iterations!
System.out.println("\nUsing two iterators at once:");
Iterator<String> iter1 = repository.createIterator();
Iterator<String> iter2 = repository.createIterator();
while (iter1.hasNext() && iter2.hasNext()) {
System.out.println(iter1.next() + " and " + iter2.next());
}
}
}

Example 3: Custom Collection with Different Traversal Orders

Building a collection that supports multiple iteration strategies (forward, backward, filtered).

NumberCollection.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Aggregate with multiple iterator types
public class NumberCollection {
private int[] numbers;
public NumberCollection(int[] numbers) {
this.numbers = numbers;
}
// Forward iterator
public Iterator<Integer> forwardIterator() {
return new ForwardIterator();
}
// Backward iterator
public Iterator<Integer> backwardIterator() {
return new BackwardIterator();
}
// Even numbers only iterator
public Iterator<Integer> evenIterator() {
return new EvenIterator();
}
// Forward Iterator
private class ForwardIterator implements Iterator<Integer> {
private int index = 0;
@Override
public boolean hasNext() {
return index < numbers.length;
}
@Override
public Integer next() {
return numbers[index++];
}
}
// Backward Iterator
private class BackwardIterator implements Iterator<Integer> {
private int index = numbers.length - 1;
@Override
public boolean hasNext() {
return index >= 0;
}
@Override
public Integer next() {
return numbers[index--];
}
}
// Filtered Iterator (only even numbers)
private class EvenIterator implements Iterator<Integer> {
private int index = 0;
@Override
public boolean hasNext() {
// Find next even number
while (index < numbers.length && numbers[index] % 2 != 0) {
index++;
}
return index < numbers.length;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new IndexOutOfBoundsException("No more even numbers");
}
return numbers[index++];
}
}
}
NumberCollectionDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Client code demonstrating different traversals
public class NumberCollectionDemo {
public static void main(String[] args) {
NumberCollection collection = new NumberCollection(
new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
);
// Forward iteration
System.out.println("Forward iteration:");
Iterator<Integer> forward = collection.forwardIterator();
while (forward.hasNext()) {
System.out.print(forward.next() + " ");
}
System.out.println();
// Backward iteration
System.out.println("\nBackward iteration:");
Iterator<Integer> backward = collection.backwardIterator();
while (backward.hasNext()) {
System.out.print(backward.next() + " ");
}
System.out.println();
// Even numbers only
System.out.println("\nEven numbers only:");
Iterator<Integer> even = collection.evenIterator();
while (even.hasNext()) {
System.out.print(even.next() + " ");
}
System.out.println();
// All three iterators work independently!
System.out.println("\nUsing all three at once:");
Iterator<Integer> f = collection.forwardIterator();
Iterator<Integer> b = collection.backwardIterator();
Iterator<Integer> e = collection.evenIterator();
while (f.hasNext() || b.hasNext() || e.hasNext()) {
if (f.hasNext()) System.out.print("F:" + f.next() + " ");
if (b.hasNext()) System.out.print("B:" + b.next() + " ");
if (e.hasNext()) System.out.print("E:" + e.next() + " ");
System.out.println();
}
}
}

🌍 Real-World Examples

  • 📚Java Collections: Iterator interface used by ArrayList, LinkedList, HashSet, etc.
  • 🗄️Database Result Sets: JDBC ResultSet iterates through query results
  • 📁File Systems: Directory iterators traverse files and folders
  • 📱Social Media Feeds: Scroll through posts one by one
  • 🎵Playlists: Music apps iterate through songs

✅ Benefits

  • Single Responsibility: Separates traversal logic from collection
  • Encapsulation: Hides internal structure of collections
  • Flexibility: Easy to implement different traversal algorithms
  • Multiple Iterations: Support concurrent traversals of the same collection
  • Uniform Interface: Same way to traverse different collection types

⚠️ Drawbacks

  • ⚠️Overkill: May be unnecessary for simple collections
  • ⚠️Performance: Can be less efficient than direct access for some collections
  • ⚠️Complexity: Adds extra classes and interfaces

🔑 Key Points to Remember

  • 1️⃣Iterator provides sequential access without exposing internal structure
  • 2️⃣hasNext() checks if more elements exist, next() returns the next element
  • 3️⃣Java Collections Framework heavily uses the Iterator pattern
  • 4️⃣Iterator is separate from the collection - can have multiple iterators
  • 5️⃣Different from Composite - Iterator traverses, Composite organizes hierarchies

💪 Practice Scenarios

  • Create an iterator for a binary tree (in-order, pre-order, post-order traversals)
  • Build an iterator for a social network graph that traverses friends
  • Implement a filtering iterator that skips certain elements
  • Design a reverse iterator that goes backward through a collection
  • Create a merged iterator that combines multiple collections