gpt4 book ai didi

lambda - 为什么在不安全操作之后而不是在整个 forEach 循环之后抛出 ConcurrentModificationException ?

转载 作者:行者123 更新时间:2023-12-02 09:06:20 27 4
gpt4 key购买 nike

通常,当迭代正在进行时进行修改时,会立即引发 ConcurrentModificationException。

但是,使用流 API 的 forEachOrdered 方法,每次修改后都不会抛出异常。相反,在迭代过程中可以成功修改,并最终在整个操作完成后抛出异常。有人可以向我解释这是怎么发生的吗?

下面的代码生成 3 个范围从 -0.5 到 0.5 的随机数。然后过滤掉负值。

        HashMap<Integer, Double> positiveNegative = new HashMap<>();
for (int i = 0; i < 3; i++) {
double value =Math.random() - 0.5;
positiveNegative.put(i, value);
}

System.out.println(positiveNegative);

//filter out the negative value
positiveNegative.entrySet().stream()
.filter((entry) -> (entry.getValue() < 0))
.forEachOrdered(entry -> {
System.out.println(entry);
positiveNegative.remove(entry.getKey());
System.out.println(positiveNegative);
});

System.out.println(positiveNegative);
{0=-0.38785299800912576, 1=-0.11085161629013052, 2=-0.3516129030809366}
0=-0.38785299800912576
{1=-0.11085161629013052, 2=-0.3516129030809366}
1=-0.11085161629013052
{2=-0.3516129030809366}
2=-0.3516129030809366
{}

Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1751)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEachOrdered(ReferencePipeline.java:502)
at Solution.main(Solution.java:51)

最佳答案

the documentation说:

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.

您的示例中不涉及并发,但要点仍然存在;此功能仅用于检测错误,而不是保证是否或何时引发异常。要检测错误,只要抛出异常就足够了,即使仅在迭代结束时也是如此。

对于 IteratorhasNext()next() 方法,无法控制调用者。调用者不需要迭代到集合的末尾,换句话说,在迭代器被放弃之前,每个方法调用都可能是最后一个方法调用。所以这些方法都不能推迟检查。

相比之下,IteratorSpliteratorforEachRemaining 方法控制迭代循环并知道它们何时退出,因此,可以省略每次迭代中的检查并在最后执行一次检查,假设我们有一个专门的实现,而不是默认方法,它别无选择,只能重复调用单元素迭代方法。

正如您在堆栈跟踪中看到的,forEachOrdered Stream 操作最终在 HashMap$EntrySpliterator.forEachRemaining 处结束,这是一个利用该机会的专门实现。显而易见的原因是性能,因为您不想浪费 CPU 周期来测试每次迭代中的条件,无论如何,当合约允许在最后执行一次时(如果我们从尽最大努力基础契约(Contract)中推断出一项要求)。

关于lambda - 为什么在不安全操作之后而不是在整个 forEach 循环之后抛出 ConcurrentModificationException ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57985323/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com