@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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…