Open In App

Iterator Design Pattern

The Iterator pattern is a widely used design pattern in software development that provides a way to access the elements of an aggregate object (such as a list or collection) sequentially without exposing its underlying representation.

Iterator-design-pattern

What is the Iterator Design Pattern?

The Iterator design pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object (like a list) sequentially without exposing its underlying representation. It defines a separate object, called an iterator, which encapsulates the details of traversing the elements of the aggregate, allowing the aggregate to change its internal structure without affecting the way its elements are accessed.

Suppose we are building an application that requires us to maintain a list of notifications. Eventually, some parts of your code will require you to iterate over all notifications. If we implemented your collection of notifications as an array you would iterate over them as:

// If a simple array is used to store notifications
for (int i = 0; i < notificationList.length; i++)
     Notification notification = notificationList[i]);


And if it were some other collection like set, tree, etc. way of iterating would change slightly. Now, what if we build an iterator that provides a generic way of iterating over a collection independent of its type.

// Create an iterator
Iterator iterator = notificationList.createIterator();
// It wouldn’t matter if list is Array or ArrayList or
// anything else.
while (iterator.hasNext())
{
    Notification notification = iterator.next());
}

Components of Iterator Design Pattern

The Iterator design pattern consists of several components:

Iterator2


1. Iterator Interface/Abstract Class

Defines the interface for accessing and traversing elements in the collection. It typically includes methods like hasNext(), next(), and optionally remove().

2. Concrete Iterator

Implements the Iterator interface and maintains the current position in the traversal of the aggregate. It provides the actual implementation for the traversal operations defined in the Iterator interface.

3. Aggregate Interface/Abstract Class

Defines the interface for creating an Iterator object. It typically includes a method like createIterator() that returns an Iterator object for the collection.

4. Concrete Aggregate

Implements the Aggregate interface and represents the collection of objects. It provides the implementation for creating an Iterator object that can traverse its elements.

Iterator Design Pattern example

Problem Statement:

Let's say we have a collection of employees in a company, and we want to iterate over the employees to calculate their total salary. However, the employees are stored in different types of collections (arrays, lists, etc.), and we want to iterate over them without exposing the underlying collection types.

Iterator1

Benefit of using the Iterator Pattern

The Iterator pattern allows us to access the elements of a collection sequentially without exposing its underlying representation. It provides a way to iterate over a collection regardless of its internal structure.

Below is the code of above problem statement using Iterator Pattern:

Let’s break down into the component wise code:

1. Iterator Interface

Defines the methods for accessing and traversing the collection.

interface Iterator<T> {
    boolean hasNext();
    T next();
}

2. Aggregate Interface

Defines the method for creating an iterator.

interface Aggregate<T> {
    Iterator<T> createIterator();
}

3. Concrete Iterator

Implements the Iterator interface and provides the actual iteration logic.

class EmployeeIterator implements Iterator<Employee> {
    private int currentIndex = 0;
    private List<Employee> employees;

    public EmployeeIterator(List<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < employees.size();
    }

    @Override
    public Employee next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return employees.get(currentIndex++);
    }
}

4. Concrete Aggregate

Implements the Aggregate interface and provides the method to create an iterator for the collection.

class Company implements Aggregate<Employee> {
    private List<Employee> employees;

    public Company(List<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public Iterator<Employee> createIterator() {
        return new EmployeeIterator(employees);
    }
}

Complete code for the above example

This code demonstrates how the Iterator pattern can be used to iterate over a collection of employees in a company, regardless of the internal storage of the employees. Below is the complete code for the above example:

import java.util.*;

// Employee class
class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }
}

// Iterator interface
interface Iterator<T> {
    boolean hasNext();
    T next();
}

// Aggregate interface
interface Aggregate<T> {
    Iterator<T> createIterator();
}

// Concrete Iterator
class EmployeeIterator implements Iterator<Employee> {
    private int currentIndex = 0;
    private List<Employee> employees;

    public EmployeeIterator(List<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < employees.size();
    }

    @Override
    public Employee next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return employees.get(currentIndex++);
    }
}

// Concrete Aggregate
class Company implements Aggregate<Employee> {
    private List<Employee> employees;

    public Company(List<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public Iterator<Employee> createIterator() {
        return new EmployeeIterator(employees);
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("Alice", 50000));
        employees.add(new Employee("Bob", 60000));
        employees.add(new Employee("Charlie", 70000));

        Company company = new Company(employees);
        Iterator<Employee> iterator = company.createIterator();

        double totalSalary = 0;
        while (iterator.hasNext()) {
            totalSalary += iterator.next().getSalary();
        }

        System.out.println("Total salary: " + totalSalary);
    }
}
class Employee {
    constructor(name, salary) {
        this.name = name;
        this.salary = salary;
    }
}
// Iterator interface
class Iterator {
    constructor(collection) {
        this.collection = collection;
        this.currentIndex = 0;
    }
    hasNext() {
        return this.currentIndex < this.collection.length;
    }
    next() {
        if (!this.hasNext()) {
            throw new Error('No such element');
        }
        return this.collection[this.currentIndex++];
    }
}
// Aggregate interface
class Aggregate {
    createIterator() {
        return null;
    }
}
// Concrete Iterator
class EmployeeIterator extends Iterator {
    constructor(collection) {
        super(collection);
    }
}
// Concrete Aggregate
class Company extends Aggregate {
    constructor(collection) {
        super();
        this.collection = collection;
    }
    createIterator() {
        return new EmployeeIterator(this.collection);
    }
}
// Main function
function main() {
    const employees = [
        new Employee("Alice", 50000),
        new Employee("Bob", 60000),
        new Employee("Charlie", 70000)
    ];
    const company = new Company(employees);
    const iterator = company.createIterator();
    let totalSalary = 0;
    while (iterator.hasNext()) {
        totalSalary += iterator.next().salary;
    }
    console.log("Total salary: " + totalSalary);
}
main();
Total salary: 180000.0

When to use Iterator Design Pattern

When to not use Iterator Design Pattern


Article Tags :