我有以下列表integers
(所有数字从0到999,999):
List<Integer> integers = new ArrayList<>();
for (int i = 0; i != 10_000_000; ++i) {
integers.add(i);
}
我尝试将以下内容作为Java 8 Stream运行:
int sum = 0;
for (Integer i : integers) {
sum = i % 2 == 0 ? i - sum : i + sum;
}
System.out.println(sum);
我期待以下输出:
0 - 0 = 0
1 + 0 = 1
2 - 1 = 1
3 + 1 = 4
4 - 4 = 0
5 + 0 = 5
6 - 5 = 1
7 + 1 = 8
8 - 8 = 0
...
999,998 - 999,997 = 1
999,999 + 1 = 10,000,000
10,000,000
(顺便说一下,有人可以用数学表达这个吗?我不能......)
如果我这样做:
int sum = integers.stream().reduce(
0,
(sum, i) -> i % 2 == 0 ? i - sum : i + sum
);
sum
是预期金额10,000,000
。
但是,如果我将流更改为并行:
int sum = integers.parallelStream().reduce(
0,
(sum, i) -> i % 2 == 0 ? i - sum : i + sum
);
sum
是0
!
我似乎无法弄清楚为什么会这样,有人可以解释一下吗?
答案 0 :(得分:6)
reduce
的Javadoc:
使用关联累加函数
对此流的元素进行缩减
注意“associative”这个词:这是你的还原功能所不具备的属性。
关联性是并行化的关键:操作的应用顺序没有定义,没有关联性,结果在重新排序时不会是不变的。
如果您有两个以上availableProcessors
,您可以使用以下代码来说服自己,答案取决于子任务的数量(请注意,您不应该使用10_000_000作为问题大小,因为它有它的因子分解中有两个;使用10_000_001):
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "2");
System.out.println(IntStream.range(0,10_000_001).parallel().reduce(0,
(sum, i) -> i % 2 == 0 ? i - sum : i + sum
));
当您更改系统属性的值时,结果也会更改。