Understanding the Iterator Pattern in Java
In this post, let’s dive into most commonly used design pattern used for accessing elements of a Collection object.
The Iterator pattern is a widely-used design pattern that provides a way to access the elements of a collection object in sequential manner without exposing its underlying representation.
Java’s Collection Framework prominently uses this pattern, enabling easy traversal and operations on data structures.
What’s the Need?
Imagine having a collection, like an array or linked list. While these structures store data efficiently, the way to access their data might differ.
The Iterator pattern abstracts this access mechanism, allowing you to focus on processing the data rather than on how to fetch it.
Core Components
- Iterator: Interface defining the methods required for iteration (
hasNext()
,next()
, etc.) - ConcreteIterator: Implementation of the Iterator interface.
- Aggregate: Interface defining methods to create and obtain iterators.
- ConcreteAggregate: Implementation of the Aggregate interface, usually representing the collection.
Java’s Built-in Support
Java already supports the Iterator pattern in its Collection Framework. Every collection has an iterator()
method that returns an Iterator
object, which can be used to traverse the collection.
For instance:
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Implementing a Custom Iterator
Let’s create our own simple collection and iterator for demonstration:
Aggregate Interface
public interface Container {
Iterator getIterator();
}
Iterator Interface
public interface CustomIterator {
boolean hasNext();
Object next();
}
Concrete Collection (e.g., a NameRepository)
public class NameRepository implements Container {
private String names[] = {"Rob", "Julie", "Lora", "John"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements CustomIterator {
int index;
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public Object next() {
if (this.hasNext()) {
return names[index++];
}
return null;
}
}
}
Using the Iterator
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
Iterator iter = namesRepository.getIterator();
while (iter.hasNext()) {
String name = (String) iter.next();
System.out.println("Name: " + name);
}
}
}
Advantages
- Single Responsibility Principle: It separates the responsibility of iteration from the actual collection.
- Flexibility: Easy to add different types of iterators or traversal strategies without changing the collection.
- Uniformity: Provides a uniform way to traverse different collections.
Conclusion
The Iterator pattern offers a clean and coherent way to traverse complex data structures. By abstracting the traversal mechanism, it promotes decoupling, readability, and flexibility in code. 🚀
Enjoyed the article? Give it a clap 👏 and share it with others who might find it useful! Get notified on new posts by following this blog.
Further Learning at DSAGuide.com
For an in-depth exploration of data structures, algorithms, and coding interview solutions, I invite you to visit DSAGuide.com. Elevate your understanding with our detailed guides on essential data structures, algorithms and coding interview problem solving.