Iterator Design Pattern

According to GoF definition, an iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It is behavioral design pattern.

As name implies, iterator helps in traversing the collection of objects in a defined manner which is useful the client applications. During iteration, client programs can perform various other operations on the elements as per requirements.

1. When to use iterator design pattern

Every programming language support some data structures like list or maps, which are used to store a group of related objects. In Java, we have List, Map and Set interfaces and their implementations such as ArrayList and HashMap.

A collection is only useful when it’s provides a way to access its elements without exposing its internal structure. The iterators bear this responsibility.

So any time, we have collection of objects and clients need a way to iterate over each collection elements in some proper sequence, we must use iterator pattern to design the solution.

The iterator pattern allow us to design a collection iterator in such a way that –

  • we are able to access elements of a collection without exposing the internal structure of elements or collection itself.
  • iterator supports multiple simultaneous traversals of a collection from start to end in forward, backward or both directions.
  • iterator provide a uniform interface for traversing different collection types transparently.

The key idea is to take the responsibility for access and traversal out of the aggregate object and put it into an Iterator object that defines a standard traversal protocol.

2. Real world example of iterator pattern

  • In Java, we have java.util.Iterator interface and it’s specific implementations such as ListIterator. All Java collections provide some internal implementations of Iterator interface which is used to iterate over collection elements.
    	List<String> names = Arrays.asList("alex", "brian", "charles");
    			
    	Iterator<String> namesIterator = names.iterator();
    	
    	while (namesIterator.hasNext()) 
    	{
    		String currentName = namesIterator.next();
    		
    		System.out.println(currentName);
    }
    
  • In media players, we have a list of songs listed and we can play the song by traversing to the songs list and select desired song. It’s also an iterator example.

3. Iterator design pattern

3.1. Architecture

Iterator Pattern Class Diagram
Iterator Pattern Class Diagram

3.2. Design participants

The participants of iterator pattern are as follows:

  • Iterator: An interface to access or traverse the elements collection. Provide methods which concrete iterators must implement.
  • ConcreteIterator: implements the Iterator interface methods. It can also keep track of the current position in the traversal of the aggregate collection.
  • Aggregate: It is typically a collection interface which defines a method that can create an Iterator object.
  • ConcreteAggregate: It implements the Aggregate interface and its specific method returns an instance of ConcreteIterator.

4. Iterator design pattern example

In this iterator pattern example, we are creating a collection which can holds instances of Token class and will provide an iterator to iterate over tokens collections in a sequence.

public class Topic 
{
	private String name;
	
	public Topic(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
public interface Iterator<E> 
{
	void reset();	// reset to the first element

	E next();	// To get the next element

	E currentItem();	// To retrieve the current element

	boolean hasNext();	// To check whether there is any next element or not.
}
public class TopicIterator implements Iterator<Topic> {
	
	private Topic[] topics;
    private int position;
    
    public TopicIterator(Topic[] topics)
    {
        this.topics = topics;
        position = 0;
    }

	@Override
	public void reset() {
		position = 0;
	}

	@Override
	public Topic next() { 
		return topics[position++];
	}

	@Override
	public Topic currentItem() {
		return topics[position];
	}

	@Override
	public boolean hasNext() {
		if(position >= topics.length)
            return false;
        return true;
	}
}
public interface List<E>
{
	Iterator<E> iterator();
}
public class TopicList implements List<Topic>
{
	private Topic[] topics;
	
    public TopicList(Topic[] topics)
    {
        this.topics = topics;
    }
    
	@Override
	public Iterator<Topic> iterator() {
		return new TopicIterator(topics);
	}
}

The client code to use the iterator will be like this.

public class Main 
{
	public static void main(String[] args) 
	{
		Topic[] topics = new Topic[5];
		topics[0] = new Topic("topic1");
		topics[1] = new Topic("topic2");
		topics[2] = new Topic("topic3");
		topics[3] = new Topic("topic4");
		topics[4] = new Topic("topic5");
		
		List<Topic> list = new TopicList(topics);
		
		Iterator<Topic> iterator = list.iterator();
		
		while(iterator.hasNext()) {
			Topic currentTopic = iterator.next();
			System.out.println(currentTopic.getName());
		}
	}
}

Program output.

topic1
topic2
topic3
topic4
topic5

Drop me your questions related to iterator pattern in comments.

Happy Learning !!

Ref : Wikipedia

Comments

Subscribe
Notify of
guest
3 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode