I want to remove all items from someMap
which keys are not present in someList
. Take a look into my code:
someMap.keySet().stream().filter(v -> !someList.contains(v)).forEach(someMap::remove);
I receive java.util.ConcurrentModificationException
. Why? Stream is not parallel. What is the most elegant way to do this?
You don't need the
Stream
API for that. UseretainAll
on thekeySet
. Any changes on theSet
returned bykeySet()
are reflected in the originalMap
.Later answer, but you could insert a collector into your pipeline so that forEach is operating on a Set which holds a copy of the keys:
Your stream call is (logically) doing the same as:
If you run this, you will find it throws
ConcurrentModificationException
, because it is modifying the map at the same time as you're iterating over it. If you have a look at the docs, you'll notice the following:This is what you are doing, the map implementation you're using evidently has fail-fast iterators, therefore this exception is being thrown.
One possible alternative is to remove the items using the iterator directly:
@Eran already explained how to solve this problem better. I will explain why
ConcurrentModificationException
occurs.The
ConcurrentModificationException
occurs because you are modifying the stream source. YourMap
is likely to beHashMap
orTreeMap
or other non-concurrent map. Let's assume it's aHashMap
. Every stream is backed bySpliterator
. If spliterator has noIMMUTABLE
andCONCURRENT
characteristics, then, as documentation says:So the
HashMap.keySet().spliterator()
is notIMMUTABLE
(because thisSet
can be modified) and notCONCURRENT
(concurrent updates are unsafe forHashMap
). So it just detects the concurrent changes and throws aConcurrentModificationException
as spliterator documentation prescribes.Also it worth citing the
HashMap
documentation:While it says about iterators only, I believe it's the same for spliterators.