Iterator Design Pattern Java

Vibhor Pareek
Javarevisited
Published in
3 min readJun 22, 2024

Introduction

The iterator design pattern is a behavioral design pattern in GOF patterns. Whenever you want to iterate over a collection or group of objects, you will always think of iterator pattern [Traverse in a defined manner]. It hides the necessary implementation from client and just iterates over it reducing complexity on client side.

The idea is to take the responsibility for access and traversal out of the actual[aggregator] object and give it to iterator object defining a custom traversal [logic in any direction/ forward/backward or any custom logic]
It satisfies Open Closed Principle & helps to provide decoupling of classes.
You will find actual implementation in java.util package for List iterators/ Map iterators to quantify the above arguments.
Enough of the talking lets look at the Code in depth.

Problem

Lets take a simple traversal for a library, where in you will traverse a list of books present in the library. I am keeping the class simple, you can have any custom logic based on shelf where book is placed or keep shelves array as input & define your logic.

Implementation

  1. Iterator Interface

The interface defines key operations which will be implemented by concrete iterators.

public interface Iterator {

boolean hasNext(); // Do you have a next element to traverse ?
Object next(); // Give me the element ?

}

2. Concrete Iterator

Concrete implementation of the above interface.
It takes a List<Book> as an argument in constructor and defines the actual traversal logic

import java.util.List;

public class BookIterator implements Iterator{

private final List<Book> bookList;

private int position;

public BookIterator(List<Book> bookList) {
this.bookList = bookList;
position = 0;
}


@Override
public boolean hasNext() {
return position < bookList.size();
}

@Override
public Object next() {
Book book = bookList.get(position);
position++;
return book;
}
}

3. Aggregator Interface

Interface defining the iterator we want to use for the traversal logic and in the actual concrete aggregator class, we will return the exact concrete implementation of iterator which is our BookIterator.

public interface Aggregate {

Iterator getIterator();

}

4. Concrete Aggregator Class

public class ConcreteAggregator implements Aggregate{

public List<Book> bookList;

public ConcreteAggregator(List<Book> bookList) {
this.bookList = bookList;
}

@Override
public Iterator getIterator() {
return new BookIterator(bookList);
}
}

5. Client Class

Lets look at how client class makes a call to Concrete Aggregator and does not bother about traversal logic but in turn gives responsibility to iterator to traverse.

public class Client {

public static void main(String[] args) {
Book book1 = Book.builder().bookName("Book1").bookType("SCIFI").bookId("1").build();
Book book2 = Book.builder().bookName("Book2").bookType("HORROR").bookId("2").build();
Book book3 = Book.builder().bookName("Book3").bookType("FICTION").bookId("3").build();
Book book4 = Book.builder().bookName("Book4").bookType("PSYCHOLOGY").bookId("4").build();

//create a list;
List<Book> bookList = new ArrayList<>();
bookList.add(book1);
bookList.add(book2);
bookList.add(book3);
bookList.add(book4);


Aggregate aggregate = new ConcreteAggregator(bookList);

// Get Book Iterator via polymorphism
Iterator iterator = aggregate.getIterator();

while(iterator.hasNext()) {
System.out.println( "Getting book : " + iterator.next().toString());
}


}
}

// Output :

// Getting book : Book(bookId=1, bookName=Book1, bookType=SCIFI)
// Getting book : Book(bookId=2, bookName=Book2, bookType=HORROR)
// Getting book : Book(bookId=3, bookName=Book3, bookType=FICTION)
// Getting book : Book(bookId=4, bookName=Book4, bookType=PSYCHOLOGY)

Conclusion

  • Decoupling & Code Reusability: We have decoupled classes with iterator and aggregator here. You can have multiple implementation and attach different iterators to aggregate object.
  • Abstraction : The iterator pattern hides the underlying data structure of a collection.
  • Good Control: The iterator pattern lets you control the iteration process. For instance, you can choose to skip certain elements in a collection, something that’s hard to do with standard loops.

--

--