Java 8流顺序执行和并行执行产生不同的结果?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (18)

在Java8中运行以下流示例:

    System.out.println(Stream
        .of("a", "b", "c", "d", "e", "f")
        .reduce("", (s1, s2) -> s1 + "/" + s2)
    );

yields:

/a/b/c/d/e/f

当然,这并不奇怪。由于http://docs.oracle.com/javase/8/docs/api/index.html?overview-summary.html流的顺序执行还是并行执行都无关紧要:

除了标识为明确不确定性的操作(如findAny())之外,顺序执行还是并行执行都不应改变计算结果。

AFAIK reduce()是确定性的并且(s1, s2) -> s1 + "/" + s2是关联的,所以添加parallel()应该产生相同的结果:

    System.out.println(Stream
            .of("a", "b", "c", "d", "e", "f")
            .parallel()
            .reduce("", (s1, s2) -> s1 + "/" + s2)
    );

但是我的机器上的结果是:

/a//b//c//d//e//f

这里有什么问题?

提问于
用户回答回答于

来自reduce的文档:

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

你的情况并非如此 - “”和“a”创建“/ a”。

我已经提取了累加器函数并添加了一个打印输出来显示发生了什么:

BinaryOperator<String> accumulator = (s1, s2) -> {
    System.out.println("joining \"" + s1 + "\" and \"" + s2 + "\"");
    return s1 + "/" + s2;
};
System.out.println(Stream
                .of("a", "b", "c", "d", "e", "f")
                .parallel()
                .reduce("", accumulator)
);

这是示例输出(它在运行之间有所不同):

joining "" and "d"
joining "" and "f"
joining "" and "b"
joining "" and "a"
joining "" and "c"
joining "" and "e"
joining "/b" and "/c"
joining "/e" and "/f"
joining "/a" and "/b//c"
joining "/d" and "/e//f"
joining "/a//b//c" and "/d//e//f"
/a//b//c//d//e//f

你可以在你的函数中添加一个if语句来分别处理空字符串:

System.out.println(Stream
        .of("a", "b", "c", "d", "e", "f")
        .parallel()
        .reduce((s1, s2) -> s1.isEmpty()? s2 : s1 + "/" + s2)
);

正如Marko Topolnik注意到的那样,检查s2并不是必需的,因为累加器不必是交换功能。

用户回答回答于

你可能想要使用可变缩减,文档指定做类似的事情

String concatenated = strings.reduce("", String::concat)

或者可以使用StringBuilder来代替。

扫码关注云+社区