并行流可能会产生与Java 8中的串行流不同的结果吗?根据我的信息,并行流与串行流相同,除了分成多个子流。这是一个速度问题。完成对元素的所有操作,并在最后组合子流的结果。最后,在我看来,对于并行和串行流,操作的结果应该是相同的。所以我的问题是,这段代码可能会给我一个不同的结果吗?如果有可能,为什么会发生?
int[] i = {1, 2, 5, 10, 9, 7, 25, 24, 26, 34, 21, 23, 23, 25, 27, 852, 654, 25, 58};
Double serial = Arrays.stream(i).filter(si -> {
return si > 5;
}).mapToDouble(Double::new).map(NewClass::add).reduce(Math::atan2).getAsDouble();
Double parallel = Arrays.stream(i).filter(si -> {
return si > 5;
}).parallel().mapToDouble(Double::new).map(NewClass::add).reduce(Math::atan2).getAsDouble();
System.out.println("serial: " + serial);
System.out.println("parallel: " + parallel);
public static double add(double i) {
return i + 0.005;
}
结果是:
serial: 3.6971567726175894E-23
parallel: 0.779264049587662
答案 0 :(得分:13)
reduce()
的javadoc说:
使用关联累积函数对此流的元素进行缩减,[...]累加器函数必须是关联函数。
单词"associative"与此java doc链接:
如果符合以下条件,则运算符或函数op是关联的:
(a op b) op c == a op (b op c)
如果我们将其扩展到四个术语,可以看出这对并行评估的重要性:
a op b op c op d == (a op b) op (c op d)
因此我们可以与(c op d)并行地评估(a op b),然后在结果上调用op。
关联操作的示例包括数字加法,最小值和最大值以及字符串连接。
正如@PaulBoddington在评论中提到的,atan2
不是关联的,因此对减少操作无效。
相依
您的流序列有点偏。你应该在并行操作之后过滤,lambda可以缩短,你不应该把双重包装:
double parallel = Arrays.stream(i)
.parallel() // <-- before filter
.filter(si -> si > 5) // <-- shorter
.asDoubleStream() // <-- not boxing
.reduce(Math::atan2)
.getAsDouble();
答案 1 :(得分:4)
将reduce
与并行流一起使用时,操作不会按特定顺序执行。
因此,如果您希望并行流产生可预测的结果,那么无论顺序如何,您的reduce操作都必须具有相同的答案。
例如,减少使用添加是有意义的,因为添加是关联的。无论你做了哪些,在两种情况下答案都是6
。
(1 + 2) + 3
1 + (2 + 3)
atan2
不是关联的。
Math.atan2(Math.atan2(1, 2), 3) == 0.15333604941031637
,而
Math.atan2(1, Math.atan2(2, 3)) == 1.0392451500584097
答案 2 :(得分:3)
如果元素以不同的顺序给出,则reduce方法会产生不同的结果。
因此,如果您使用并行流,则原始订单不会保证。
如果你使用不同的缩小方法(例如(x,y) - > x> y)它可以正常工作。