Explain Codes LogoExplain Codes Logo

Calling remove in foreach loop in Java

java
concurrent-modification-exception
iterator
collections
Alex KataevbyAlex Kataev·Jan 31, 2025
TLDR
// Fun fact: Iterators and collections have a love-hate relationship 🤷‍ List<String> myList = new ArrayList<>(Arrays.asList("a", "b", "c")); Iterator<String> iterator = myList.iterator(); while (iterator.hasNext()) { String s = iterator.next(); if ("b".equals(s)) { iterator.remove(); // Relationship status: It's complicated! } } // Result: myList = ["a", "c"]

Key: The romance between the iterator and collections is tricky. Like in life, always use iterator.remove() to make a safely modify in your journey without any heartbreaking ConcurrentModificationException.

Behind the scenes of safe removal

Java Collections are a temperamental lot. Try to fiddle around while you're playing games with them, and you get an infamous ConcurrentModificationException. This roll of thunder happens when you attempt to use a foreach loop, which tactfully enforces an Iterator in the background. If the collection modulates outside of this iterator's control, the Iterator swiftly throws a tantrum!

Demystifying multi-criteria removal

Life isn't always as simple as black or white, especially when you have complex removal logic that demands more than simple equivalence checks. In that case, you need to suit up and dive into the realm of complex operations such as string length evaluation or pattern matching:

List<String> complexList = new ArrayList<>(Arrays.asList("ant", "elephant", "dinosaur")); complexList.removeIf(s -> s.length() > 6); // Java 8 came up with a balm for your complex pains! // Skipping the lengthy stuff: ["ant"]

The stage-stealing removeIf method of Java 8 inevitably saves the day as it safely eliminates elements using a predicate, without the fear of facing the dreadful ConcurrentModificationException.

Sequrity

Making friends with CopyOnWriteArrayList

Java list can throw a ConcurrentModificationException even with the classic iterator in multi-threaded environment. Enter CopyOnWriteArrayList, your friendly neighborhood Spider...err, thread-safe alternative:

CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>(Arrays.asList("a", "b", "c")); for (String item : cowList) { cowList.remove(item); // This. Is. Thread-safe! } // Result: cowList = Leisurely napping, because it's an empty list now

However, with great CopyOnWriteArrayList comes the not-so-great performance cost. So, tread cautiously!

Post-iteration removal: Collection's tattoo removal!

When you are in a spot, and using an iterator is as appealing as a root canal, you could opt for collecting items separately to remove post-iteration, much like saving all your receipts for that one big spring clean:

List<String> items = new ArrayList<>(Arrays.asList("space", "earth", "mars")); List<String> toRemove = new ArrayList<>(); for (String item : items) { if (item.contains("a")) { toRemove.add(item); // Did someone just declare war on the letter 'a'? } } items.removeAll(toRemove); // Picture this as a giant magnet pulling out the metal pieces // Result: items = ["space"]

LinkedHashSet: The exterminator for duplicates

Pestering problem of duplicates hounding your collection? Fret not, meet LinkedHashSet.

List<String> duplicatesList = new ArrayList<>(Arrays.asList("apple", "banana", "apple", "kiwi", "banana")); Set<String> uniqueElements = new LinkedHashSet<>(duplicatesList); // Result: uniqueElements = ["apple", "banana", "kiwi"], preserving original glory

LinkedHashSet is your friendly neighborhood exterminator service, removing duplicates whilst preserving the insertion order.