The Iterator Pattern is a behavioral design pattern that allows you to traverse elements of a collection (like a List, Set, or Tree) without exposing the underlying internal structure of that collection.
The Problem
Imagine you have a Social Network website. You store your users in a complex Graph structure, but you also have some users stored in a simple Array.
If a client wants to print a list of all user names, they would need two different loops: one for the Array and a much more complex recursive one for the Graph. Every time you change how you store data, the client has to rewrite their loops.
The Solution: The "Scanner"
The Iterator pattern extracts the traversal logic into a separate object called an Iterator. The client doesn't care if the data is in an Array, a List, or a Tree; they just ask the Iterator for the next() element until there are none left (hasNext()).
Step-by-Step Java Implementation
1. The Iterator Interface
This defines the methods required to move through a collection.
// MyIterator.java
public interface MyIterator {
boolean hasNext();
Object next();
}
2. The Collection Interface
This defines a method to get the iterator.
// MyCollection.java
public interface MyCollection {
MyIterator createIterator();
}
3. Concrete Collection & Iterator
The collection stores the data, and the iterator knows how to walk through it.
// NameCollection.java
public class NameCollection implements MyCollection {
private String[] names = {"Ahmed", "Sara", "John", "Doe"};
@Override
public MyIterator createIterator() {
return new NameIterator();
}
// Inner class that knows how to traverse the array
private class NameIterator implements MyIterator {
int index = 0;
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public Object next() {
if (this.hasNext()) {
return names[index++];
}
return null;
}
}
}
Full Code for Testing
// Save as IteratorTest.java
// 1. Iterator Interface
interface Iterator {
boolean hasNext();
Object next();
}
// 2. Aggregate Interface
interface Container {
Iterator getIterator();
}
// 3. Concrete Implementation
class Repository implements Container {
public String[] items = {"Java", "Python", "C++", "Quantum Computing"};
@Override
public Iterator getIterator() {
return new ItemIterator();
}
private class ItemIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
return index < items.length;
}
@Override
public Object next() {
if (this.hasNext()) {
return items[index++];
}
return null;
}
}
}
// 4. Main Test
public class IteratorTest {
public static void main(String[] args) {
System.out.println("--- Iterator Pattern Test ---\n");
Repository repo = new Repository();
// The client doesn't even know 'items' is an Array!
for (Iterator iter = repo.getIterator(); iter.hasNext();) {
String name = (String) iter.next();
System.out.println("Item: " + name);
}
System.out.println("\n--- Traversal Complete ---");
}
}
Why use Iterator?
- Single Responsibility: You move the traversal logic out of the collection and into the iterator.
- Uniformity: You can use the same loop structure to iterate over very different data structures.
- Parallel Traversal: You can have multiple iterators going through the same collection at the same time (e.g., one starting from the beginning and one from the end).
Real-World Example
- Java Collections Framework: Every time you use a
for-eachloop in Java (for (String s : list)), Java is actually using the Iterator Pattern behind the scenes. - Database Cursors: When you query a database, the result set is often delivered as an iterator-like object that lets you scroll through rows one by one.