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 Interface - defines traversal methodspublic interface Iterator<T> { boolean hasNext(); T next();}// Aggregate Interface - defines method to create iteratorpublic interface Container<T> { Iterator<T> createIterator();}// The element we're storingpublic 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; }}// Concrete Aggregate - the collectionpublic 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); }}// Concrete Iterator - implements traversalpublic 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; }}// Client code - uses the iteratorpublic 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.
// Concrete Aggregate with internal storagepublic 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++]; } }}// Client codepublic 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).
// Aggregate with multiple iterator typespublic 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++]; } }}// Client code demonstrating different traversalspublic 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