Web Analytics

Java Iterators

Intermediate ~15 min read

An Iterator is a design pattern that provides a standard way to traverse any Collection without exposing its internal structure. It's essential for safely modifying collections during iteration.

Java Iterator Pattern and Safe Collection Modification

Why Use Iterators?

  • Uniform traversal: Same approach works for any Collection
  • Safe removal: Only way to remove elements during iteration
  • Encapsulation: Don't need to know internal structure
  • Fail-fast: Detects concurrent modifications immediately

The Iterator Interface

public interface Iterator<E> {
    boolean hasNext();  // Returns true if more elements exist
    E next();           // Returns next element, advances cursor
    void remove();      // Removes last element returned by next()

    // Java 8+ default method
    void forEachRemaining(Consumer<? super E> action);
}

Basic Usage

import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");

// Get iterator
Iterator<String> it = names.iterator();

// Classic iteration pattern
while (it.hasNext()) {
    String name = it.next();
    System.out.println(name);
}

// Java 8+ forEachRemaining
Iterator<String> it2 = names.iterator();
it2.forEachRemaining(name -> System.out.println(name));

Safe Removal During Iteration

This is the main reason to use Iterator over for-each loops:

List<Integer> numbers = new ArrayList<>(
    Arrays.asList(1, 5, 12, 3, 8, 15, 7, 20)
);

// CORRECT: Using Iterator.remove()
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer num = it.next();
    if (num > 10) {
        it.remove();  // Safe! Removes current element
    }
}
// numbers is now [1, 5, 3, 8, 7]

// WRONG: Modifying during for-each
for (Integer num : numbers) {
    if (num > 10) {
        numbers.remove(num);  // Throws ConcurrentModificationException!
    }
}
ConcurrentModificationException: This occurs when a collection is modified while iterating with for-each. Always use Iterator.remove() or Java 8+ removeIf() instead.
Output
Click Run to execute your code

ListIterator (For Lists Only)

ListIterator extends Iterator with bidirectional traversal and modification:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
ListIterator<String> lit = list.listIterator();

// Forward traversal
while (lit.hasNext()) {
    int index = lit.nextIndex();
    String element = lit.next();
    System.out.println(index + ": " + element);
}

// Backward traversal
while (lit.hasPrevious()) {
    int index = lit.previousIndex();
    String element = lit.previous();
    System.out.println(index + ": " + element);
}

// Start from specific index
ListIterator<String> fromMiddle = list.listIterator(2); // Start at index 2

// Modify during iteration
ListIterator<String> modifier = list.listIterator();
while (modifier.hasNext()) {
    String s = modifier.next();
    if (s.equals("B")) {
        modifier.set("B-modified");  // Replace current element
    }
    if (s.equals("C")) {
        modifier.add("C-extra");     // Add after current element
    }
}

Java 8+ Alternatives

List<Integer> numbers = new ArrayList<>(
    Arrays.asList(1, 5, 12, 3, 8, 15, 7, 20)
);

// removeIf() - Cleaner than Iterator for simple removal
numbers.removeIf(n -> n > 10);  // Same result as Iterator example

// Stream filter (creates new collection)
List<Integer> filtered = numbers.stream()
    .filter(n -> n <= 10)
    .collect(Collectors.toList());

// forEach with lambda
numbers.forEach(n -> System.out.println(n));

// replaceAll
numbers.replaceAll(n -> n * 2);  // Double all values

When to Use Iterator vs For-Each

Use Case Best Choice
Simple traversal (read-only) for-each loop
Need to remove elements Iterator or removeIf()
Need current index Traditional for loop or ListIterator
Bidirectional traversal ListIterator
Add elements during iteration ListIterator
Parallel processing Stream API

The Iterable Interface

Any class implementing Iterable can be used in for-each loops:

public class NumberRange implements Iterable<Integer> {
    private final int start;
    private final int end;

    public NumberRange(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int current = start;

            @Override
            public boolean hasNext() {
                return current <= end;
            }

            @Override
            public Integer next() {
                return current++;
            }
        };
    }
}

// Now usable in for-each!
for (int num : new NumberRange(1, 5)) {
    System.out.println(num);  // 1, 2, 3, 4, 5
}

Summary

  • Iterator provides safe, uniform collection traversal
  • Use Iterator.remove() to safely remove during iteration
  • ListIterator adds bidirectional traversal and modification
  • For-each uses Iterator internally but doesn't allow removal
  • Java 8+: Consider removeIf(), forEach(), and Streams
  • Implement Iterable to make custom classes for-each compatible