Explain Codes LogoExplain Codes Logo

How to avoid "ConcurrentModificationException" while removing elements from ArrayList while iterating it?

java
concurrent-modification
arraylist
multithreading
Nikita BarsukovbyNikita Barsukov·Aug 14, 2024
TLDR

Dodge the ConcurrentModificationException using Iterator or ListIterator:

// Let's play safe with Iterator! Iterator<String> itr = list.iterator(); while (itr.hasNext()) { if ("b".equals(itr.next())) itr.remove(); // Bye bye "b" }

Prefer back-and-forth access? Say hi to ListIterator:

// I can go back and come forward. No biggie! ListIterator<String> litr = list.listIterator(); while (litr.hasNext()) { if ("b".equals(litr.next())) litr.remove(); // "b", it wasn't meant to be… }

In Java 8 and higher, befriend the removeIf function:

// Just a "b"? I have my methods to exterminate you! list.removeIf("b"::equals);

Making the changes during iteration ensures safety and no exception stress!

Delving deeper: Let your fingers do the coding

The classic loop: Reverse for to the rescue

Doing list.remove(item) within a forward for loop is a no-no; it's like stepping on a rake. Instead, go backwards:

// Ready for some time travel? for (int i = list.size() - 1; i >= 0; i--) { if (condition) { list.remove(i); } }

Removing from the back won't shift the elements you haven't iterated yet.

Guard your ArrayList in a multi-thread environment

If your ArrayList is a field in a multi-threaded class, synchronize your code or face the „music“ (also known as the exception we're discussing):

// Wearing my protective gear synchronized (list) { Iterator<String> itr = list.iterator(); while (itr.hasNext()) { if ("b".equals(itr.next())) itr.remove(); // Now "b" is dancing solo } }

The Army Knife for concurrency: CopyOnWriteArrayList

In a multithreaded context, makeCopyOnWriteArrayList your buddy:

// Abracadabra - Say hello to my thread-safe friend! CopyOnWriteArrayList<Type> cowList = new CopyOnWriteArrayList<>(list);

But remember, this is only for frequent-reads, infrequent-writes. Otherwise, prepare for a performance hit.

Tweak for special cases

Need more performance in your concurrent setup? Use either Collections.synchronizedList or ConcurrentHashMap—your call!

Tips, tricks, and when to use what

Is it a bird, is it a plane, it's batch removal with removeAll!

// Ready, set ... remove all! list.removeAll(Arrays.asList(*removableElements));

Initializing ArrayList with values

Want to preload some values into your list? Here you go:

// Start with a bang. No time to wait! ArrayList<Type> myList = new ArrayList<>(Arrays.asList(values));

Deciphering the mystery of Exceptions

When your console lights up with red, it's time to unseal the scroll of stack trace. Fear not, your key is the method just before the Exception.

Don't just code, code smartly

Iterate with caution using for-each

While for-each loop is handy, it can lead you to a trap. For any modification plans, stick to iterators or streams.

CopyOnWriteArrayList is handy for lots of reads and sparse writes. Certainly, not a good choice for everyday, single-threaded tasks.

The removeIf function is your friend if the situation involves predicates. It's like an expressway to clean code:

// I don't like dislike you, I just don't need you! list.removeIf(e -> e.isUnwanted());

Lastly, remember that the size of the collection and the cost of the conditional check determines the performance of the method chosen.