Contents
- Comparison Table
- Using Iterator
- Using ListIterator
- ConcurrentModificationException
- When to Use Which
| Feature | Iterator | ListIterator |
| Applicable to | Any Collection (List, Set, Queue…) | Only List implementations |
| Direction | Forward only | Forward and backward |
| Obtain via | collection.iterator() | list.listIterator() |
| Add elements | ❌ No | ✅ add(E) |
| Replace elements | ❌ No | ✅ set(E) |
| Get current index | ❌ No | ✅ nextIndex() / previousIndex() |
| Remove elements | ✅ remove() | ✅ remove() |
Obtain an Iterator from any Collection via collection.iterator().
The three core methods are hasNext(), next(), and remove().
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]
Always call next() before remove(). Calling remove() twice without an intervening next() throws IllegalStateException.
ListIterator extends Iterator and adds reverse traversal, element replacement, and insertion.
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.
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.
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.
- 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.