我想从 someMap
中删除 someList
中不存在的所有项。
看看我的代码:
someMap.keySet()
.stream()
.filter(v -> !someList.contains(v))
.forEach(someMap::remove);
我收到java.util.ConcurrentModificationException
。
鉴于流不是并行的,为什么我会遇到此异常?
最优雅的方法是什么?
最佳答案
@Eran 已经 explained如何更好的解决这个问题。我将解释为什么会发生 ConcurrentModificationException
。
发生ConcurrentModificationException
是因为您正在修改流源。您的 Map
可能是 HashMap
或 TreeMap
或其他非并发映射。我们假设它是一个HashMap
。每个流都由 Spliterator
支持。如果 spliterator 没有 IMMUTABLE
和 CONCURRENT
特征,那么,正如文档所述:
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.
因此,HashMap.keySet().spliterator()
不是 IMMUTABLE
(因为这个 Set
可以修改),也不是 CONCURRENT
(并发更新对于 HashMap
来说是不安全的)。因此,它只是检测并发更改并按照 spliterator 文档的规定抛出 ConcurrentModificationException
。
还值得引用HashMap
文档:
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.
虽然它只提到了迭代器,但我相信它对于分割器来说也是一样的。
关于java - 使用具有 Maps 键集的流时出现 ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52121150/