java - 使用具有 Maps 键集的流时出现 ConcurrentModificationException

标签 java foreach lambda hashmap java-stream

我想从 someMap 中删除 someList 中不存在的所有项。
看看我的代码:

someMap.keySet()
    .stream()
    .filter(v -> !someList.contains(v))
    .forEach(someMap::remove);

我收到java.util.ConcurrentModificationException
鉴于流不是并行的,为什么我会遇到此异常?
最优雅的方法是什么?

最佳答案

@Eran 已经 explained如何更好的解决这个问题。我将解释为什么会发生 ConcurrentModificationException

发生ConcurrentModificationException是因为您正在修改流源。您的 Map 可能是 HashMapTreeMap 或其他非并发映射。我们假设它是一个HashMap。每个流都由 Spliterator 支持。如果 spliterator 没有 IMMUTABLECONCURRENT 特征,那么,正如文档所述:

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/

相关文章:

c++ - 函数模板取函数模板

异步回调中的 C++ boost-asio-network ,哪一个更好?使用 lambda 或 boost::bind()

java - 将一个数组传播到一个函数的多个参数中

java - 方法级的 EJB 事务属性覆盖

java - 充气城堡 : Detached Enveloped Signature Changes at Every Run

php - foreach 循环在达到限制之前中断

c# - 我如何存储和重用我的 lambda 表达式片段

java - 在 UTF-8 编码代码中,使用带重音字符的字符串,该字符串取自以 ISO-8859-1 编码的文件

php - 遍历多维数组

php foreach里面for语句问题