Introduction
Imagine you are building a music streaming application.
A playlist contains hundreds of songs.
Users should be able to:
Play the next song
Check if more songs exist
Restart the playlist
Traverse songs one by one
A beginner might directly access the internal collection:
This works fine.
But what if later the internal storage changes?
Instead of a vector, you decide to use:
Linked List
Tree
Hash Table
Database Cursor
Now every piece of code accessing the collection must change.
This creates tight coupling between the client and the collection implementation.
The Iterator Design Pattern solves this problem.
It provides a standardized way to traverse a collection without exposing how the collection is internally stored.
What is the Iterator Pattern?
The Iterator Pattern is a behavioral design pattern that provides a way to access elements of a collection sequentially without exposing its internal representation.
In simple words:
Iterator allows clients to traverse a collection without knowing how the collection is implemented internally.The client only knows how to move through elements.
It does not know whether the data is stored in:
Arrays
Linked Lists
Trees
Graphs
Databases
Real World Analogy
Imagine visiting an art gallery.
The gallery contains many paintings.
As a visitor, you simply move:
To the next painting
To the previous painting
Check if more paintings exist
You don't need to know:
How paintings are stored
How rooms are organized
How inventory is maintained
The gallery guide acts like an iterator.
It provides a simple way to navigate through the collection.
Why Do We Need the Iterator Pattern?
Suppose we have a playlist class.
Client code:
At first, this seems reasonable.
But several problems appear.
Problems Without the Iterator Pattern
1. Internal Structure Is Exposed
The client directly accesses:
vector<string>
The internal implementation becomes public knowledge.
2. Tight Coupling
If storage changes:
vector → linked list
Client code may break.
3. Multiple Traversal Logic
Different clients may implement traversal differently.
This creates duplicate code.
4. Violates Encapsulation
Collections should manage their own traversal behavior.
Clients should not know internal details.
Solution: Iterator Pattern
The Iterator Pattern separates:
Collection Storage
Collection Traversal
The collection stores data.
The iterator handles traversal.
Now clients only interact with the iterator.
Key Components of the Iterator Pattern
The Iterator Pattern usually contains:
1. Iterator Interface
Defines traversal operations.
Example:
next()
hasNext()
2. Concrete Iterator
Implements traversal logic.
3. Aggregate Interface
Represents the collection.
4. Concrete Aggregate
Actual collection implementation.
Structure of the Iterator Pattern
Collection
│
▼
Create Iterator
│
▼
Iterator
│
┌───┴───┐
│ │
next() hasNext()
Example: Playlist Traversal
Let's build a simple playlist system.
Step 1: Create Iterator Interface
Step 2: Create Playlist Collection
Step 3: Create Concrete Iterator
Step 4: Client Code
Output
Song A
Song B
Song C
How the Iterator Pattern Works
Let's understand the flow.
Step 1
The collection stores data.
playlist.addSong(...)
Step 2
An iterator is created.
PlaylistIterator iterator(...)
Step 3
Client asks:
hasNext()
Step 4
An iterator returns elements one by one.
next()
The client never directly accesses internal storage.
Better Version Using Aggregate Interface
In real systems, collections usually create their own iterators.
Example:
Collection:
Client:
This further hides implementation details.
Internal Iterator vs External Iterator
A common interview topic.
External Iterator
The client controls traversal.
Example:
The client decides when to move.
Internal Iterator
The collection controls traversal.
Example:
playlist.forEach(...)
The collection decides how traversal occurs.
Modern programming languages often use internal iterators.
Iterator in STL
The C++ STL heavily uses the Iterator Pattern.
Example:
Here:
begin()
end()
iterator
are implementations of the Iterator Pattern.
This is one of the most practical examples of the pattern.
Advantages of the Iterator Pattern
1. Encapsulation
Internal collection structure remains hidden.
2. Loose Coupling
Clients do not depend on collection implementation.
3. Reusable Traversal Logic
Traversal code exists in one place.
4. Multiple Traversal Strategies
Different iterators can provide different traversals.
Example:
Forward Iterator
Reverse Iterator
Random Iterator
5. Cleaner Client Code
Clients focus on business logic rather than traversal details.
Disadvantages of the Iterator Pattern
1. Additional Classes
The pattern introduces extra objects.
2. Slightly Increased Complexity
Simple collections may not need custom iterators.
3. Performance Overhead
Very large systems may experience minor overhead due to abstraction.
Multiple Iterators for Same Collection
One powerful feature is creating multiple traversal methods.
Example:
Playlist:
Song A
Song B
Song C
Song D
Possible iterators:
Forward Iterator
A → B → C → D
Reverse Iterator
D → C → B → A
Shuffle Iterator
B → D → A → C
The collection remains unchanged.
Only traversal behavior changes.
Iterator vs Direct Access
| Direct Access | Iterator Pattern |
|---|---|
| Exposes storage | Hides storage |
| Tight coupling | Loose coupling |
| Traversal logic repeated | Traversal logic centralized |
| Difficult to change storage | Easy to change storage |
| Less flexible | More flexible |
Iterator vs Observer
This is a common interview question.
| Iterator | Observer |
|---|---|
| Traverses collections | Notifies objects |
| Accesses elements one by one | Sends updates to subscribers |
| Focuses on navigation | Focuses on communication |
| Pull-based | Push-based |
Real World Applications
The Iterator Pattern is widely used in:
STL Containers
Java Collections Framework
Database cursors
File system traversal
Tree traversal
Graph traversal
Playlist navigation
Browser history navigation
Pagination systems
Almost every collection framework internally uses iterators.
Common Beginner Mistakes
1. Exposing Internal Collections
Avoid:
vector<string>& getSongs()
Whenever possible.
Use iterators instead.
2. Mixing Traversal with Collection Logic
Collections should store data.
Iterators should traverse data.
3. Ignoring Boundary Conditions
Always check:
hasNext()
before:
next()
4. Creating One Iterator for Every Need
Sometimes standard iterators are sufficient.
Avoid unnecessary complexity.
Simple Visualization
Without Iterator:
Client
│
▼
Collection Internals
The client directly depends on storage details.
With Iterator:
Client
│
▼
Iterator
│
▼
Collection
The iterator acts as a bridge between the client and the collection.
Summary
The Iterator Design Pattern provides a standardized way to traverse collections without exposing their internal structure.
Separating traversal logic from storage logic, it improves encapsulation, flexibility, and maintainability. Clients interact with iterators instead of directly accessing collection internals, making it easier to change storage implementations without affecting existing code.
Whenever you need to traverse elements of a collection while keeping its internal implementation hidden, the Iterator Pattern offers a clean and scalable solution.