使用带有Maps键集的流时出现ConcurrentModificationException [英] ConcurrentModificationException when using stream with Maps key set
问题描述
我想删除 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
因为您正在修改流源而发生。您的地图
可能是 HashMap
或 TreeMap
或其他非并发地图。我们假设它是 HashMap
。每个流都由 Spliterator <支持/ code>
。如果spliterator没有 IMMUTABLE
和 CONCURRENT
特征,那么,正如文档所说:
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
。执行此操作的Spliterators称为 fail-fast 。
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
不安全)。所以它只检测并发更改并抛出 ConcurrentModificationException
作为spliterator文档规定。
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.
还值得引用< a href =https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html =noreferrer> HashMap
文档:
Also it worth citing the HashMap
documentation:
所有类的集合视图方法返回的迭代器都是 fail-fast :如果在创建迭代器之后的任何时候对映射进行结构修改,除了通过迭代器自己的remove方法之外,迭代器将抛出
ConcurrentModificationException
。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险。
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.
注意迭代器的快速失败行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下,不可能做出任何硬性保证。失败快速迭代器尽力抛出 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!