Iterator Design Pattern

Iterator design pattern is one of the most popular and relatively simple design pattern which is commonly used in Java programming language. Iterator pattern provides a standard way to traverse a group of objects sequentially without exposing its internal implementation. The Iterator pattern takes the responsibility of traversal out of the collection of object and put it into an Iterator object that defines how to traverse these collection of objects.

Iterator pattern is widely used in Java Collection Framework. This pattern falls under behavioral design pattern. In this tutorial, we will explore the Iterator Design Pattern in Java, covering its structure, implementation, best practices, and advantages.


Structure of the Iterator Design Pattern

The Iterator Design Pattern consists of the following components:
  • Iterator Interface : This interface declares methods for traversing the elements of a collection. It typically includes methods such as next(), hasNext(), and possibly remove().

  • Concrete Iterator : The concrete iterator implements the iterator interface and keeps track of the current position within the collection. It provides the implementation for the traversal methods.

  • Aggregate Interface : This interface declares methods for creating iterators. It may include a method like createIterator().

  • Concrete Aggregate : The concrete aggregate implements the aggregate interface and provides the actual collection of elements. It creates an iterator that corresponds to its type.

  • Client : The client is responsible for using the iterator to traverse the collection without being aware of its internal structure.

Advantages of Iterator Pattern

  • Iterator design pattern hides the implementation logic of object traversal through the collection and client programs just use iterator methods. The logic for iteration is embedded in the collection itself and it helps client program to iterate over them easily.

  • Iterator pattern supports different kind of iterators based on our requirements as long as it implements iterator interface. For example, we can implement a custom iterator to traverse the objects of a collection in increasing order.

  • Iterator pattern de-couple the algorithm logic from the data structure. Suppose there is a method which calculates the sum of all numbers provided by an iterator object. It doesn't matter whether numbers are stored in linked list or binary tree as long as Iterator objects provides a way to traverse all numbers, the method can calculate the sum.

  • The pattern separates the concerns of collection storage and iteration logic. This separation enhances modularity and allows clients to focus on the task of traversal without being concerned about the internal structure of the collection.

  • The iterator encapsulates the iteration logic, providing a clean and standardized way to traverse elements. This encapsulation promotes code reuse and ensures that iteration details are managed within the iterator.

  • Iterators can be implemented to provide efficient traversal strategies, especially for large or complex collections. Custom iterators can be tailored to optimize traversal based on specific requirements.

  • The Iterator pattern is compatible with the enhanced for loop introduced in Java 5. This allows clients to use a more concise syntax for iterating over elements without manually managing indices.

Implementation of Iterator Design Pattern

We will first create an Iterator interface which contains hasNext and next navigation method and a Iterable interface which retruns the reference to iterator object.

Iterator Design Pattern UML Diagram Iterator.java
  • hasNext() : Returns true if the iteration has more elements.
  • next(): Returns the next element in the iteration.
public interface Iterator {
    public boolean hasNext();
    public Object next();
}

Iterable.java
  • getIterator() : Returns an implementation of Iterator interface for traversal over.
public interface Iterable {
    public Iterator getIterator();
}

ProductCatalog.java

The ProductCatalog class contains a sting array storing product names will implement Iterable interface to provide an iterator object to traverse product catalog.The ProductCatalog class contains an inner class ProductIterator which implements Iterator interface for the traversal of product catalog. ProductIterator class contains the logic regarding how to traverse product catalog, which is hidden from the client.

public class ProductCatalog implements Iterable{
    public String productCatalog[] = {"Toothbrush","Soap",
        "Toothpaste","Sampoo","Handwash"};

    public Iterator getIterator() {
        return new ProductIterator();
    }

    private class ProductIterator implements Iterator {
        int index;
        @Override
        public boolean hasNext() {
            if(index < productCatalog.length){
                return true;
            }
            return false;
        }

        @Override
        public Object next() {
            if(this.hasNext()){
                return productCatalog[index++];
            }
            return null;
        }
    }
}

IteratorPatternExample.java
public class IteratorPatternExample {
    public static void main(String[] args) {
        ProductCatalog productCatalog = new ProductCatalog();
        Iterator iterator = productCatalog.getIterator();
        while(iterator.hasNext()) {
            System.out.println("Product Name : " + iterator.next());
        }
    }
}

Output

Product Name : Toothbrush
Product Name : Soap
Product Name : Toothpaste
Product Name : Sampoo
Product Name : Handwash

Best Practices of Iterator Design Pattern

  • Use generic iterators to make the pattern more flexible and applicable to various types of collections. Generics allow the iterator to work with different types of elements.

  • Ensure consistency in the iterator interfaces across different collections. Iterator interfaces should follow a common pattern, making it easier for clients to work with multiple collections using a similar approach.

  • Enable a collection to facilitate the concurrent use of multiple iterators. This functionality proves beneficial when clients necessitate traversing the same collection employing distinct traversal strategies.

  • Keep the concerns of collection and iteration separate. The collection should focus on storing elements, while the iterator should handle the traversal logic. This separation enhances modularity.

  • Consider making iterators immutable to prevent unintended modifications during iteration. Immutability ensures that the state of the iterator remains constant throughout the traversal process.

  • Clearly document the behavior of iterators, including their traversal order, handling of concurrent modifications, and any other relevant details. Documentation helps clients use iterators correctly.

  • If modifications to the collection during iteration are not supported, consider implementing fail-fast iterators. Fail-fast iterators throw exceptions if the collection is modified while an iteration is in progress.

  • If the collection supports modifications during iteration, handle concurrent modifications gracefully. This may involve using mechanisms such as locks or iterators that are aware of modifications.

  • For collections with specific traversal requirements or optimizations, consider providing custom iterators. Custom iterators can be tailored to address unique scenarios.
Related Topics
Abstract Factory Design Pattern
Builder Design Pattern
Strategy Design Pattern
State Design Pattern
Flyweight Design Pattern
Filter Design Pattern