3.5 - Iterator Pattern: Behavioral Design Patterns

The Iterator Pattern is a behavioral design pattern that provides a way to sequentially access the elements of a collection without exposing the underlying structure. It separates the process of traversal from the actual collection, promoting loose coupling between the collection and the client.

The Iterator Pattern is useful when you need to iterate through a collection of objects without knowing its internal structure or implementation details, making it ideal for use with lists, arrays, trees, and more.

Key Components of the Iterator Pattern:

  1. Collection Interface: Declares the methods to add, remove, and create an iterator for the collection.

  2. Concrete Collection: Implements the collection interface and defines how the elements are stored and managed.

  3. Iterator Interface: Declares the methods to traverse elements of a collection.

  4. Concrete Iterator: Implements the iterator interface and provides mechanisms to traverse the specific collection.

  5. Client: Uses the collection and iterator to access the elements.

Code Example

Here’s an implementation of the Iterator Pattern using renamed class and function names for clarity.

1. Collection Interface

The ItemCollection interface declares methods for adding and removing items from the collection, as well as a method to create an iterator.

// Collection Interface
interface ItemCollection<T> {
    void addElement(T element);
    void removeElement(T element);
    Iterator<T> createIterator();
}

2. Concrete Collection

The ItemList class implements the ItemCollection interface and uses an ArrayList internally to store elements. It also provides functionality to create an iterator for the collection.

// Concrete Collection Class
class ItemList<T> implements ItemCollection<T> {
    private List<T> elements = new ArrayList<>();

    @Override
    public void addElement(T element) {
        elements.add(element);
    }

    @Override
    public void removeElement(T element) {
        elements.remove(element);
    }

    @Override
    public Iterator<T> createIterator() {
        return elements.iterator(); // Using Java's built-in iterator for simplicity
    }
}

3. Client

The IteratorDemo class demonstrates how to use the ItemList and its iterator to access the elements of the collection.

// Client Class
public class IteratorDemo {
    public static void main(String[] args) {
        ItemCollection<String> myCollection = new ItemList<>(); // Creating the collection

        // Adding elements to the collection
        myCollection.addElement("Apple");
        myCollection.addElement("Banana");
        myCollection.addElement("Cherry");

        // Creating an iterator to traverse the collection
        Iterator<String> iterator = myCollection.createIterator();

        // Using the iterator to access elements
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Explanation:

  1. ItemCollection Interface: The interface defines three methods—addElement(), removeElement(), and createIterator(). The createIterator() method returns an iterator for traversing the elements in the collection.

  2. ItemList Class: This is the concrete implementation of the ItemCollection interface. It uses a list (ArrayList) to store the elements and provides the necessary methods to add, remove, and create an iterator.

  3. Iterator: In this case, we’re using Java’s built-in Iterator, which provides the basic functionality for traversing the elements in the collection. The iterator offers methods such as hasNext() to check if more elements are available and next() to access the next element.

  4. Client (IteratorDemo): This class demonstrates how the collection and iterator work together. After adding elements to the collection, the client uses the iterator to access and print each element sequentially.

Benefits of the Iterator Pattern:

  1. Decoupling: The iterator pattern decouples the client from the internal structure of the collection. The client does not need to know how the collection is implemented or how elements are stored; it simply uses the iterator to access elements.

  2. Uniform Traversal: Different types of collections (e.g., lists, arrays, trees) can provide their own iterators, allowing clients to traverse them in a uniform manner.

  3. Flexibility: Iterators provide a flexible way to traverse a collection in various ways, such as forward, backward, or even skipping elements, without changing the collection’s structure.

  4. Single Responsibility: The iterator pattern follows the Single Responsibility Principle, as the collection is responsible for storing and managing elements, while the iterator is responsible for traversal.

Real-World Use Cases:

  • Java Collections: In Java, all the Collection classes (e.g., ArrayList, HashSet) use the iterator pattern. The Iterator interface in Java allows you to iterate over any collection without knowing its specific implementation.

  • File Systems: Iterators can be used to traverse files and directories, providing a way to access files in a file system structure.

  • Tree Structures: In data structures such as trees, different types of iterators (e.g., pre-order, in-order, post-order) can be used to traverse the nodes.

Conclusion:

The Iterator Pattern is a useful tool when you want to provide a uniform way to traverse different types of collections without exposing their internal implementation. It allows for more flexible and scalable code, as it separates the collection’s responsibility for storing elements from the logic of iterating over them. By using iterators, you can create a clean, maintainable way to access elements in any type of collection.

1
Subscribe to my newsletter

Read articles from Venu Madhav Emmadi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Venu Madhav Emmadi
Venu Madhav Emmadi