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?
How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.
Since Iterator of HashMap is fail-fast it will throw ConcurrentModificationException if you try to remove entry using Map.
We can also avoid the Concurrent Modification Exception in a single threaded environment. We can use the remove() method of Iterator to remove the object from the underlying collection object. But in this case, you can remove only the same object and not any other object from the list.
@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. Your Map
is likely to be HashMap
or TreeMap
or other non-concurrent map. Let's assume it's a HashMap
. Every stream is backed by Spliterator
. If spliterator has no IMMUTABLE
and CONCURRENT
characteristics, then, as documentation says:
After binding a Spliterator should, on a best-effort basis, throw
ConcurrentModificationException
if structural interference is detected. Spliterators that do this are called fail-fast.
So the HashMap.keySet().spliterator()
is not IMMUTABLE
(because this Set
can be modified) and not CONCURRENT
(concurrent updates are unsafe for HashMap
). So it just detects the concurrent changes and throws a ConcurrentModificationException
as spliterator documentation prescribes.
Also it worth citing the HashMap
documentation:
The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a
ConcurrentModificationException
. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw
ConcurrentModificationException
on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
While it says about iterators only, I believe it's the same for spliterators.
You don't need the Stream
API for that. Use retainAll
on the keySet
. Any changes on the Set
returned by keySet()
are reflected in the original Map
.
someMap.keySet().retainAll(someList);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With