并发模式使用具有地图键集的流时的异常

2022-09-01 19:45:52

我想删除 所有键不存在的项目。看看我的代码:someMapsomeList

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

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


答案 1

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

发生这种情况是因为您正在修改流源。您的可能是 或 或其他非并发映射。让我们假设它是一个.每个流都由Spliterator支持。如果分路器没有和特性,那么,正如文档所说:ConcurrentModificationExceptionMapHashMapTreeMapHashMapIMMUTABLECONCURRENT

绑定后,如果检测到结构干涉,则应尽最大努力投掷分路器。执行此操作的拆分器称为快速故障ConcurrentModificationException

因此,不是(因为这可以修改)也不是(并发更新对于 )因此,它只是检测并发更改并抛出一个 as spliterator 文档规定。HashMap.keySet().spliterator()IMMUTABLESetCONCURRENTHashMapConcurrentModificationException

同样值得引用HashMap文档:

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

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

虽然它只说迭代器,但我相信对于拆分器也是如此。


答案 2

您不需要API。在 上使用。返回的任何更改都将反映在原始 .StreamretainAllkeySetSetkeySet()Map

someMap.keySet().retainAll(someList);