Java8流顺序和并行执行产生不同的结果? [英] Java8 streams sequential and parallel execution produce different results?

查看:78
本文介绍了Java8流顺序和并行执行产生不同的结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Java8中运行以下流示例:

Running the following stream example in Java8:

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

收益率:

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

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

Which is - of course - no surprise. Due to http://docs.oracle.com/javase/8/docs/api/index.html?overview-summary.html it shouldn't matter whether the stream is executed sequentially or parallel:


除了标识为显式非确定性的操作(例如findAny())之外,流是顺序执行还是并行执行不应更改计算结果。

Except for operations identified as explicitly nondeterministic, such as findAny(), whether a stream executes sequentially or in parallel should not change the result of the computation.

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

AFAIK reduce() is deterministic and (s1, s2) -> s1 + "/" + s2 is associative, so that adding parallel() should yield the same result:

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

然而,我机器上的结果是:

However the result on my machine is:

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

这里有什么问题?

BTW:使用(首选) .collect(Collectors.joining(/) )而不是 reduce(...)产生相同的结果 a / b / c / d / e / f 用于顺序执行和并行执行。

BTW: using (the preferred) .collect(Collectors.joining("/")) instead of reduce(...) yields the same result a/b/c/d/e/f for sequential and parallel execution.

JVM详细信息:

java.specification.version: 1.8
java.version: 1.8.0_31
java.vm.version: 25.31-b07
java.runtime.version: 1.8.0_31-b13


推荐答案

来自reduce的文档:

From reduce's documentation:


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

The identity value must be an identity for the accumulator function. This means that for all t, accumulator.apply(identity, t) is equal to t.

在你的情况下不是这样 - 和a创建/ a。

Which is not true in your case - "" and "a" creates "/a".

我已经提取累加器函数并添加了一个打印输出以显示发生的情况:

I have extracted the accumulator function and added a printout to show what happens:

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)
);

这是示例输出(运行之间不同):

This is example output (it differs between runs):

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语句来分别处理空字符串:

You can add an if statement to your function to handle empty string separately:

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

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

As Marko Topolnik noticed, checking s2 is not required as accumulator doesn't have to be commutative function.

这篇关于Java8流顺序和并行执行产生不同的结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆