Iterator Design Pattern in Java
1. Introduction
The Iterator Pattern provides a way to sequentially access elements of a collection without exposing its internal structure.
Instead of the client needing to know whether a collection is an array, list, or tree, the iterator hides this complexity and provides a simple interface to loop through elements.
2. Real-Life Analogy
Think about a TV remote:
- You press the next channel button, and it switches to the next channel in sequence.
- You don’t need to know how channels are stored or processed internally; you just move to the next one.
Similarly, in collections, the iterator gives you access to elements one by one without exposing the underlying details.
3. Structure
- Iterator (Interface) → Defines methods like hasNext() and next().
- ConcreteIterator → Implements the Iterator for a collection.
- Aggregate (Collection Interface) → Defines a method to create an iterator.
- ConcreteAggregate (Custom Collection) → Implements the collection and returns its iterator.
4. Custom Java Example: Playlist Iterator
We’ll create a Playlist (custom collection) that stores songs and an Iterator to loop through them.
Step 1: Iterator Interface
interface Iterator {
boolean hasNext();
T next();
}
Step 2: Aggregate (Collection) Interface
interface IterableCollection {
Iterator createIterator();
}
Step 3: Concrete Collection (Playlist)
import java.util.ArrayList;
import java.util.List;
class Playlist implements IterableCollection {
private List songs = new ArrayList<>();
public void addSong(String song) {
songs.add(song);
}
@Override
public Iterator createIterator() {
return new PlaylistIterator(songs);
}
}
Step 4: Concrete Iterator (PlaylistIterator)
class PlaylistIterator implements Iterator {
private List songs;
private int position = 0;
public PlaylistIterator(List songs) {
this.songs = songs;
}
@Override
public boolean hasNext() {
return position < songs.size();
}
@Override
public String next() {
return songs.get(position++);
}
}
Step 5: Client Code
public class IteratorPatternDemo {
public static void main(String[] args) {
Playlist playlist = new Playlist();
playlist.addSong("Shape of You");
playlist.addSong("Blinding Lights");
playlist.addSong("Perfect");
playlist.addSong("Levitating");
Iterator iterator = playlist.createIterator();
System.out.println("Playlist:");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
5. Output
Playlist:
Shape of You
Blinding Lights
Perfect
Levitating
6. Benefits
- Provides a uniform way to iterate over different collections.
- Hides the internal representation (array, list, tree, etc.).
- Makes code cleaner and more maintainable.
7. When to Use
- When you want to provide a consistent way to iterate over a collection.
- When the internal structure of the collection should remain hidden.
- Useful when creating custom collections.