Iterator Pattern
The Iterator pattern is a behavioral design pattern that provides a way to access elements of a collection sequentially or in an order without exposing its underlying representation. It separates the traversal of a collection from its structure, allowing for cleaner, more modular code.
The Iterator pattern shines when dealing with complex collections or when you need multiple ways to traverse a collection. It provides a clean separation of concerns and makes your code more maintainable and flexible. Each collection can have various iterators, each providing a different way to access the elements, encapsulating the traversal details (like the algorithm used), current element/position, and the number of elements left to traverse in the collection. Also, multiple iterators can be used to iterate over a collection simultaneously.
The Iterator pattern is widely used in Java, most notably in the Java Collections Framework. For example, the java.util.Iterator
interface is the core of this pattern in Java, and many collection classes provide implementations of this interface.
Structure
The Iterator pattern consists of the following key components:
- Iterator: is an interface that declares the methods for accessing and traversing elements of a collection
- Concrete Iterator: is a concrete implementation of the Iterator interface, defines the algorithms to traverse the elements and maintain the position of the current element
- Aggregate: is an interface that declares methods to get iterators specific to the collection
- Concrete Aggregate: concrete implementation of the Aggregate interface and returns an instance of Concrete Iterators
- Client: uses the Iterator and Aggregate interface to work on the collection and iterators
When to use the Iterator Pattern?
- When you want to separate the collection and iterator implementations (
Single Responsibility Principle
) - When you want to add new types of collections or iterators without breaking your existing code (
Open/Closed Principle
) - When you want to access elements of a collection in different/multiple ways without dealing with the implementation details
Implementation Example
The code below demonstrates the implementation of the Iterator design pattern in Java. It defines a Solution
class with a main method that creates a ConcreteAggregate
object, adds items to it, and then uses an iterator to traverse and print these items. The code includes an Iterator
interface with hasNext()
and next()
methods, implemented by ConcreteIterator
. There’s also an Aggregate
interface with a createIterator()
method, implemented by ConcreteAggregate
. The ConcreteAggregate
class manages a list of items and provides a method to create an iterator for those items. This structure allows for sequential access to the elements of the aggregate object without exposing its underlying representation.
import java.util.List;
import java.util.ArrayList;
// Client class
public class Solution {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
aggregate.addItem("Item 1");
aggregate.addItem("Item 2");
aggregate.addItem("Item 3");
aggregate.addItem("Item 4");
aggregate.addItem("Item 5");
Iterator iterator = aggregate.createIterator();
System.out.println("Iterating over the items in aggregate!");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("Done iterating!");
}
}
// Iterator interface
interface Iterator {
boolean hasNext();
String next();
}
// Concrete Iterator
class ConcreteIterator implements Iterator {
private int position = 0;
private List<String> items;
public ConcreteIterator(List<String> items) {
this.items = items;
}
public boolean hasNext() {
return position < items.size();
}
public String next() {
if (this.hasNext()) {
return items.get(position++);
}
return null;
}
}
// Aggregate interface
interface Aggregate {
void addItem(String item);
Iterator createIterator();
}
// Concrete Aggregate
class ConcreteAggregate implements Aggregate {
private List<String> items = new ArrayList<>();
public void addItem(String item) {
items.add(item);
}
public Iterator createIterator() {
return new ConcreteIterator(items);
}
}
The output of the following program will be:
Iterating over the items in aggregate!
Item 1
Item 2
Item 3
Item 4
Item 5
Done iterating!
Enjoyed the read? Give it a thumbs up 👍🏻!