首页 > 解决方案 > 难道不能保证从列表派生的并行流总是表现得像它的顺序对应物一样,提供相同的、可预测的输出吗?

问题描述

以下代码打印了 100 次 true:

for(int i=0; i<100; i++) {
   String s2 = Arrays.asList("A", "E", "I", "O", "U").parallelStream().reduce("x", String::concat, String::concat);
   System.out.println("xAxExIxOxU".equals(s2));
}

当然,100 次并不是保证。但是,即使这里使用的身份不符合要求“...对于所有 u,combiner.apply(identity, u) 等于 u” 我们仍然可以说,难道不是吗?从列表或任何其他固有有序结构派生的并行流的行为就像 reduce() 中返回相同输出的顺序流?

标签: javajava-8java-stream

解决方案


带有标识参数的函数JavadocStream.reduce说:

标识值必须是累加器函数的标识。这意味着对于所有 t,accumulator.apply(identity, t) 等于 t。

这显然不是这里的情况 -"x".concat(anything)不等于anything。这里唯一有效的身份值是""

如果您已经测试了问题标题的前提- 通过查看非并行操作返回的内容 - 您会看到标题的答案是“否” - 因为"xAEIOU"您的 reduce 操作返回了非并行流。

如果您将标识值从 更改"x""",那么答案将是“是的,有这样的保证,因为您的 reduce 函数是关联的,并且对标识值的约束也得到满足。”

即使您修改了标题,答案也很明确:

reduce您通过提供一个不是您的 reduce 函数的标识值的值作为标识值来破坏函数的合同。因此,由于您违反了reduce方法的合同,因此所有保证都无效。

创建一个不成立的案例很容易;就像 Holger 已经指出的那样,让你的列表更大:

List<String> list = new ArrayList<>();
for (int i = 0; i < 500; i++) {
    list.add("A");
}
String s2 = list.parallelStream().reduce("x", String::concat, String::concat);
System.out.println(s2);
if (s2.length() != list.size() * 2) {
    System.out.println("Bad s2 size");
}

推荐阅读