Back to Blog
Java

Java Iterator remove: Safe Element Removal During Iteration

java iterator remove: Learn how to use Iterator.remove() to safely delete elements from Java collections during iteration, avoid ConcurrentModificationException, and c...

Java CollectionsIteratorConcurrentModificationExceptionremoveIfJava Loops
Illustration of Java Iterator.remove() removing an element from a collection during iteration.

When you need to remove elements from a Java collection while iterating over it, calling remove() on the collection itself throws ConcurrentModificationException. The java iterator remove method exists specifically to solve this problem. It lets you delete the current element through the iterator instead of the collection, keeping the iteration state consistent.

The Problem with Removing During Iteration

Java's fail-fast iterators are designed to detect structural modification of the underlying collection during iteration. If you call list.remove() inside a for loop that uses an iterator, the collection's modification count changes without the iterator being notified. On the next next() call, the iterator compares the expected modification count with the actual one and throws ConcurrentModificationException.

List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie")); for (String name : names) { if (name.equals("Bob")) { names.remove(name); // throws ConcurrentModificationException } }

The enhanced for loop hides the iterator, but the behavior is the same. The only safe way to remove the current element while iterating is to use the iterator's own remove() method.

How Iterator.remove() Works

The Iterator interface defines remove() as an optional operation. Its contract is simple: it removes the last element returned by next() from the underlying collection. Calling remove() before next() or twice in a row throws IllegalStateException. The critical advantage is that the iterator updates its internal expected modification count to match the collection after removal, so iteration can continue without throwing.

List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie")); Iterator<String> iterator = names.iterator(); while (iterator.hasNext()) { String name = iterator.next(); if (name.equals("Bob")) { iterator.remove(); } }

After this code runs, names contains ["Alice", "Charlie"]. The iterator remains valid and can continue processing the remaining elements.

Removing Elements with an Explicit Iterator

Using an explicit iterator gives you full control over the iteration and removal process. This is especially useful when you need to remove multiple elements or when the removal condition depends on state accumulated during iteration.

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6)); Iterator<Integer> iterator = numbers.iterator(); int sum = 0; while (iterator.hasNext()) { int value = iterator.next(); sum += value; if (sum > 6) { iterator.remove(); } }

Here, elements are removed based on a running total, something that is awkward to express with a simple condition. The explicit iterator also works with all Iterable collections, including those that do not support removeIf (though most modern collections do).

Using removeIf for Simpler Cases

Java 8 introduced Collection.removeIf(Predicate), which internally uses an iterator to remove all matching elements. This is the most concise and readable approach when the removal condition is a simple predicate.

List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie")); names.removeIf(name -> name.equals("Bob"));

removeIf is implemented in the Collection interface and works for any collection that supports removal. It is not available on arrays or on collections that do not support element removal, such as immutable collections. In those cases, you need to build a new collection or use an explicit iterator.

Handling ConcurrentModificationException

ConcurrentModificationException is not only a risk when using the enhanced for loop. It can also occur if you modify the collection from another thread while iterating. The iterator's remove() method only protects you from modifications made through the same iterator. If another thread modifies the collection, you need external synchronization or a concurrent collection like CopyOnWriteArrayList.

List<String> list = new CopyOnWriteArrayList<>(List.of("A", "B", "C")); for (String s : list) { if (s.equals("B")) { list.remove(s); // safe with CopyOnWriteArrayList } }

CopyOnWriteArrayList creates a new underlying array on every modification, so its iterator never throws ConcurrentModificationException. However, this comes at a cost: each write is O(n), making it unsuitable for write-heavy workloads.

Performance and Memory Considerations

Removing elements during iteration has different costs depending on the collection type. For ArrayList, each removal shifts subsequent elements left, making a single removal O(n). Removing many elements one by one can therefore be O(n²). For LinkedList, removal of the current element is O(1) if you have the iterator, because the iterator holds a reference to the node. In practice, removeIf is implemented efficiently for each collection type; for example, ArrayList.removeIf compacts the array in a single pass, avoiding the quadratic cost of repeated removals.

List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5)); list.removeIf(n -> n % 2 == 0); // single pass, O(n)

If you are removing many elements and memory is a concern, consider building a new list with the elements you want to keep. This uses extra memory but is often faster and simpler for large collections.

Choosing the Right Removal Approach

ApproachWhen to UseExample
Iterator.remove()When you need to remove the current element while iterating and you need the iterator's position to remain valid.Removing elements based on a running condition inside a loop.
Collection.removeIf()When the removal condition is a simple predicate and you are on Java 8 or later.Filtering a list based on a property of each element.
Stream + collectWhen you want to keep the original collection unchanged or need to transform the result.Creating a filtered copy of a collection.
CopyOnWriteArrayListWhen concurrent reads and writes are frequent and you want to avoid ConcurrentModificationException.Read-heavy workloads with occasional modifications.

Use removeIf as your default for straightforward filtering. Fall back to an explicit iterator when you need to perform additional operations during iteration, such as accumulating state or removing elements based on a condition that changes as you go. For concurrent scenarios, choose a thread-safe collection rather than relying on Iterator.remove() alone.

The java iterator remove method remains a fundamental tool for safe in-place modification of collections. Understanding its contract and limitations helps you write robust code that avoids subtle runtime exceptions and performs well under varying workloads.

java iterator remove: Practical Usage and Code Examples | RYUSLOG DEV