🔸 TL;DR
CopyOnWriteArrayList gives us a snapshot iterator that never throws ConcurrentModificationException while iterating.
But its subList() is not snapshot-based and can blow up under concurrent writes.
🔸 THE PROBLEM
Using CopyOnWriteArrayList.subList() looks safe for concurrent use, but:
▪️ If you do new ArrayList<>(list.subList(0, 2)) and another thread mutates the original CopyOnWriteArrayList, the sublist becomes invalid.
▪️ Any call on that sublist can then throw ConcurrentModificationException.
▪️ This is surprising, given that the iterator is explicitly documented as not throwing this exception.
A naive fix is:
new ArrayList<>(list).subList(0, 2);
▪️ It works, but copies the entire list just to take a small window.
▪️ For large lists, that’s unnecessary overhead.
🔸 THE IDEA: IMMUTABLE SNAPSHOT SUBLIST
We can lean on the snapshot iterator and build our own sublist:
▪️ Iterate from fromIndex to toIndex using list.listIterator(fromIndex).
▪️ Copy elements into an Object[].
▪️ Expose it as an ImmutableSubList by extending AbstractList and implementing only get(int) and size().
This gives us:
▪️ A stable, immutable snapshot of the requested range.
▪️ No ConcurrentModificationException, because we only rely on the COW iterator. (COW like CopyOnWriteArrayList, see full article below 👇)
▪️ Significantly better performance than copying the whole list or using streams (stream().limit(2).toList() was ~5x slower in my tests).
🔸 TAKEAWAYS
▪️ CopyOnWriteArrayList is safe for iteration, but not via subList().
▪️ For concurrent code, treat subList() as unsafe on COW lists.
▪️ Use an iterator-based snapshot (e.g. ImmutableSubList over AbstractList) for fast, safe, immutable slices.
▪️ Streams are elegant, but not always the most correct or fastest tool in concurrent scenarios.
#Java #JavaConcurrency #CopyOnWriteArrayList #ConcurrentProgramming #Performance #JavaDevelopers #JavaTips #ThreadSafety #Newsletter
Go further with Java certification:
Java👇
Spring👇
SpringBook👇
JavaBook👇