Java - Iterator vs ListIterator

Both Iterator and ListIterator are used to traverse collections in Java, but they differ significantly in capability. Iterator is a general-purpose, forward-only cursor for any Collection; ListIterator is a more powerful, bidirectional cursor available only for List implementations.

Contents

Comparison Table

An Iterator is a cursor you get from any Collection: it moves forward one element at a time and lets you safely remove elements while traversing. A ListIterator extends that contract with extra powers available only on List implementations: it can move backwards, replace the current element in-place, and insert new elements mid-traversal. Use Iterator for general-purpose, forward-only iteration; reach for ListIterator when you specifically need to walk a list in both directions or modify elements as you go.

FeatureIteratorListIterator
Applicable toAny Collection (List, Set, Queue…)Only List implementations
DirectionForward onlyForward and backward
Obtain viacollection.iterator()list.listIterator()
Add elements❌ No✅ add(E)
Replace elements❌ No✅ set(E)
Get current index❌ No✅ nextIndex() / previousIndex()
Remove elements✅ remove()✅ remove()

Using Iterator

Obtain an Iterator from any Collection via collection.iterator(). The three core methods are hasNext(), next(), and remove().

Java
List<String> fruits = new ArrayList<>(List.of("apple", "banana", "cherry", "date"));

Iterator<String> it = fruits.iterator();
while (it.hasNext()) {
    String fruit = it.next();
    System.out.println(fruit);

    // Safe removal during iteration (unlike list.remove() which throws CME)
    if (fruit.startsWith("b")) {
        it.remove(); // removes "banana"
    }
}
System.out.println(fruits); // [apple, cherry, date]

Note: Always call next() before remove(). Calling remove() twice without an intervening next() throws IllegalStateException.

Using ListIterator

ListIterator extends Iterator and adds reverse traversal, element replacement, and insertion.

Java
List<String> items = new ArrayList<>(List.of("a", "b", "c", "d"));

ListIterator<String> lit = items.listIterator();

// Forward pass — uppercase each element
while (lit.hasNext()) {
    int idx = lit.nextIndex();
    String val = lit.next();
    lit.set(val.toUpperCase()); // replace current element
    System.out.println("Index " + idx + ": " + val);
}
// items = [A, B, C, D]

// Backward pass — print in reverse
System.out.println("Reverse:");
while (lit.hasPrevious()) {
    System.out.print(lit.previous() + " "); // D C B A
}

// Insert an element after "B" (start from beginning, stop at B)
ListIterator<String> insertIt = items.listIterator();
while (insertIt.hasNext()) {
    if (insertIt.next().equals("B")) {
        insertIt.add("B+"); // inserted after "B"
        break;
    }
}
System.out.println(items); // [A, B, B+, C, D]

Start from a specific index: list.listIterator(index) positions the cursor before the element at that index.

ConcurrentModificationException

Both iterators are fail-fast: if the underlying collection is structurally modified (elements added or removed) through any means other than the iterator's own remove() / add() methods, a ConcurrentModificationException is thrown immediately.

Java
List<String> list = new ArrayList<>(List.of("a", "b", "c"));

// WRONG — throws ConcurrentModificationException
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("b")) {
        list.remove(s); // directly modifying the list!
    }
}

// CORRECT — use iterator's remove()
Iterator<String> it2 = list.iterator();
while (it2.hasNext()) {
    if (it2.next().equals("b")) {
        it2.remove(); // safe
    }
}

// ALTERNATIVE — Java 8+ removeIf (uses iterator internally)
list.removeIf(s -> s.equals("b"));
CopyOnWriteArrayList's iterator is fail-safe (it operates on a snapshot copy) and does not throw ConcurrentModificationException, but its remove() is unsupported.

When to Use Which

  • Use Iterator when you need to traverse any Collection (including Set and Queue) forward-only, optionally removing elements during traversal.
  • Use ListIterator when you need to:
    • Traverse a List in both directions.
    • Replace elements during traversal (set()).
    • Insert elements during traversal (add()).
    • Know the current index (nextIndex() / previousIndex()).
  • Use the enhanced for-loop (which uses Iterator under the hood) when you only need to read elements without modifying the collection.
  • Use removeIf() (Java 8+) as a clean alternative to iterator-based removal.