问题描述
我想从 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
可能是 HashMap
或 TreeMap
或其他非并发映射.让我们假设它是一个 HashMap
.每个流都由 Spliterator
支持.如果 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
.执行此操作的 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!