本文介绍了使用带有 Maps 键集的流时出现 ConcurrentModificationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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);

我收到java.util.ConcurrentModificationException.为什么?流不是并行的.最优雅的方法是什么?

I receive java.util.ConcurrentModificationException. Why? Stream is not parallel. What is the most elegant way to do this?

推荐答案

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

@Eran already explained how to solve this problem better. I will explain why ConcurrentModificationException occurs.

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

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:

在绑定 Spliterator 之后,如果检测到结构干扰,应该尽最大努力抛出 ConcurrentModificationException.执行此操作的 Spliterator 称为 fail-fast.

所以 HashMap.keySet().spliterator() 不是 IMMUTABLE(因为这个 Set 可以修改)而不是 CONCURRENT(并发更新对于 HashMap 来说是不安全的).因此,它只是检测并发更改并按照拆分器文档的规定抛出 ConcurrentModificationException.

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.

另外值得一提的是HashMap 文档:

Also it worth citing the HashMap documentation:

该类的所有集合视图方法"返回的迭代器are fail-fast:如果在迭代器创建后的任何时间对地图进行结构修改,除了通过迭代器自己的remove方法之外的任何方式,迭代器都会抛出一个ConcurrentModificationException.因此,面对并发修改,迭代器会快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险.

请注意,无法保证迭代器的快速失败行为,因为一般而言,在存在非同步并发修改的情况下不可能做出任何硬保证.快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException.因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误.

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.

这篇关于使用带有 Maps 键集的流时出现 ConcurrentModificationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-03 21:42