Iterator Design Pattern in Java – Complete Guide
ZeeshanAli-0704

ZeeshanAli-0704 @zeeshanali0704

About: I have expertise in developing and unit-testing web applications using JavaScript, React-Redux, Angular, Node.js, Next.js and KnockoutJS frameworks.

Location:
INDIA
Joined:
Aug 13, 2022

Iterator Design Pattern in Java – Complete Guide

Publish Date: Jun 22
0 0

📚 Table of Contents


🌀 Iterator Design Pattern in Java – Explained with a Custom User Management Example

📌 What is the Iterator Design Pattern?

The Iterator Design Pattern is a behavioral design pattern that provides a way to sequentially access elements of a collection without exposing its internal structure.

Think of it as a clean way to loop through complex data structures like trees, graphs, or even simple collections, without the caller needing to know how the elements are stored internally.


🧑‍🤝‍🧑 Key Participants

In the context of our example:

Pattern Role Java Class
Iterator MyIterator
Concrete Iterator MyIneratorImpl
Aggregate UserManagement
Concrete Elements User

🛒 Real world Analogy

Think of user management in a web app. The admin doesn’t care how users are stored (array, list, map). They just want to browse user profiles one by one — next user, next user...

That’s what the iterator does — lets the admin move across users without exposing the backend logic.


📊 UML Diagram (Text Format)

        +--------------------+       +------------------------+
        |    MyIterator      |<------+    MyIneratorImpl      |
        +--------------------+       +------------------------+
        | +hasNext():boolean |       | -list: List<User>      |
        | +next(): Object     |       | -index: int            |
        +--------------------+       | +hasNext()             |
                                      | +next()                |
                                      +------------------------+

        +------------------------+
        |   UserManagement       |
        +------------------------+
        | -list: List<User>      |
        | +addUser(User)         |
        | +getIterator():Iterator|
        +------------------------+

        +-------------+
        |   User      |
        +-------------+
        | -name       |
        | -emailID    |
        | +getName()  |
        | +getEmailID()|
        +-------------+
Enter fullscreen mode Exit fullscreen mode

💻 Full Java Implementation

1. User – The Element Object

package org.example;

public class User {
    private String name;
    private String emailID;

    public User(String name, String emailID) {
        this.name = name;
        this.emailID = emailID;
    }

    public String getName() {
        return name;
    }

    public String getEmailID() {
        return emailID;
    }
}
Enter fullscreen mode Exit fullscreen mode

2. MyIterator – The Iterator Interface

package org.example;

public interface MyIterator {
    boolean hasNext();
    Object next();
}
Enter fullscreen mode Exit fullscreen mode

3. MyIneratorImpl – Concrete Iterator

package org.example;

import java.util.List;

public class MyIneratorImpl implements MyIterator {
    private List<User> list;
    private int index;

    public MyIneratorImpl(List<User> list) {
        this.list = list;
        this.index = 0;
    }

    public boolean hasNext() {
        return index < list.size();
    }

    public Object next() {
        return list.get(index++);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. UserManagement – Aggregate Class

package org.example;

import java.util.ArrayList;

public class UserManagement {
    private ArrayList<User> list = new ArrayList<>();

    public void addUser(User user) {
        list.add(user);
    }

    public MyIterator getIterator() {
        return new MyIneratorImpl(list);
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Main – Client Code

package org.example;

public class Main {
    public static void main(String[] args) {
        UserManagement userManagement = new UserManagement();

        User user1 = new User("User1", "user1@gmail.com");
        User user2 = new User("User2", "user2@gmail.com");

        userManagement.addUser(user1);
        userManagement.addUser(user2);

        MyIterator iterator = userManagement.getIterator();

        while (iterator.hasNext()) {
            User user = (User) iterator.next();
            System.out.println(user.getName() + " - " + user.getEmailID());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🛠 Use Cases in Real-World Systems

  • Traversing elements in custom collection classes
  • Paging through user records, log entries, or audit history
  • Abstracting tree traversal in file systems or DOM trees
  • Iterating over results of complex computations

✅ Advantages

  • Decouples iteration logic from collection
  • Allows different traversal strategies (e.g., reverse, conditional)
  • Enables multiple iterators on same collection
  • Supports lazy evaluation if needed

❌ Disadvantages

  • Additional boilerplate code for small or simple collections
  • Requires maintenance of custom iterator classes
  • Can be error-prone if next() used without hasNext()

🧭 When to Use and When Not To Use

✅ Use When:

  • You want to traverse a collection in a standard way
  • Internal data structure should remain hidden
  • Multiple types of iteration are needed

❌ Avoid When:

  • Java’s built-in Iterator or enhanced for-loop suffices
  • Performance is critical and abstraction adds overhead
  • Collections are simple and don’t require abstraction

🔄 Internal vs External Iterators

Type Description Java Example
External Client controls iteration (our example) Custom iterator, while (hasNext)
Internal Collection controls iteration forEach(), Streams API

🔍 Comparison with Java Built-in Iterators

Feature Our Custom MyIterator Java's Built-in Iterator
Interface name MyIterator java.util.Iterator
Generic support ❌ (Object) ✅ (Iterator<T>)
Additional methods ✅ (remove(), forEachRemaining)
Loop compatibility ✅ (Iterable) supports for-each
Preferred in practice ✅ (less code, more power)

🧑‍🏫 Best Practices and Pitfalls

✅ Best Practices:

  • Add type safety with generics (MyIterator<T>)
  • Check hasNext() before calling next()
  • Prefer Iterable<T> interface to support enhanced for-loop
  • Keep iterator logic inside collection class if possible

⚠️ Pitfalls:

  • Don’t call next() without hasNext() — may cause IndexOutOfBoundsException
  • Don’t modify list during iteration (unless supported)
  • Avoid exposing mutable internal data via next()

🔁 Alternatives

Alternative Use When
Enhanced for-loop When implementing Iterable<T>
Java Streams When using functional and lazy operations
ListIterator When bidirectional or modifying iteration is needed
Reactive Streams For async event-based data flow

📝 Summary (Bullet Points)

  • The Iterator Pattern provides a standard way to iterate collections without exposing internals.
  • We implemented MyIterator, MyIneratorImpl, UserManagement, and User to demonstrate it.
  • Separation of concerns: collection logic and iteration logic are independent.
  • Java provides built-in Iterator/Iterable that are preferable in production.
  • Consider using Streams or for-each for simpler use cases.
  • Useful in designing robust and flexible custom collection classes.


📚 Explore More Design Patterns in Java


More Details:

Get all articles related to system design
Hastag: SystemDesignWithZeeshanAli

systemdesignwithzeeshanali

Git: https://github.com/ZeeshanAli-0704/SystemDesignWithZeeshanAli

Comments 0 total

    Add comment