Iteration and fail-fast

Collections · lesson 10 of 42 · 3 min read

Remove elements safely and understand why ConcurrentModificationException fires.

Open this lesson in the learning hub

Key points

  • Modifying a collection while a for-each loop is running throws ConcurrentModificationException.
  • Iterators track a modification counter. If the collection changed behind their back, they refuse to continue.
  • Fail-fast is a bug detector, not a guarantee. It sometimes misses — removing the second-to-last element ends the loop quietly instead.
  • To delete during iteration use Iterator.remove(), or better, collection.removeIf(predicate).
  • keySet(), values() and entrySet() are live views. Removing from a view removes from the map.
  • Map.Entry.setValue is the legal way to change values while iterating a map.

Example

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(List.of("ann", "bob", "cy", "dee"));
        try {
            for (String n : names) {
                if (n.equals("ann")) names.remove(n);
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("for-each + remove : ConcurrentModificationException");
            System.out.println("  list already changed: " + names);
        }

        List<String> a = new ArrayList<>(List.of("ann", "bob", "cy", "dee"));
        Iterator<String> it = a.iterator();
        while (it.hasNext()) {
            if (it.next().length() == 3) it.remove();
        }
        System.out.println("Iterator.remove   : " + a);

        List<String> b = new ArrayList<>(List.of("ann", "bob", "cy", "dee"));
        b.removeIf(n -> n.length() == 3);
        System.out.println("removeIf          : " + b);

        Map<String, Integer> m = new LinkedHashMap<>();
        m.put("a", 1); m.put("b", 2);
        for (Map.Entry<String, Integer> e : m.entrySet()) e.setValue(e.getValue() * 10);
        System.out.println("entry.setValue    : " + m);

        m.keySet().remove("a");
        System.out.println("view removal      : " + m + "   (views write through)");
    }
}

Never remove inside a for-each. Use removeIf and the problem disappears.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Collections course, and every lesson in it is listed on the Collections contents page.