Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8流顺序执行和并行执行会产生不同的结果吗?_Java_Lambda_Java 8_Java Stream - Fatal编程技术网

Java8流顺序执行和并行执行会产生不同的结果吗?

Java8流顺序执行和并行执行会产生不同的结果吗?,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,在Java8中运行以下流示例: System.out.println(Stream .of("a", "b", "c", "d", "e", "f") .reduce("", (s1, s2) -> s1 + "/" + s2) ); 收益率: /a/b/c/d/e/f 当然,这并不奇怪。 因为流是按顺序执行还是并行执行并不重要: 除了被明确标识为不确定的操作(如findAny())外,流是顺序执行还是并行执行都不应改变计算结果 AFAI

在Java8中运行以下流示例:

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

/a/b/c/d/e/f
当然,这并不奇怪。 因为流是按顺序执行还是并行执行并不重要:

除了被明确标识为不确定的操作(如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
这里怎么了

顺便说一句:使用(首选)
.collect(collector.joining(“/”)
而不是
reduce(…)
为顺序和并行执行生成相同的结果
a/b/c/d/e/f

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的文档中:

标识值必须是累加器函数的标识。这意味着对于所有t,累加器.apply(恒等式,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)
);
您可以向函数中添加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)
将产生不好的性能结果

我们将得到预期的结果,甚至可以并行工作。 然而,我们可能对这次演出不满意!这样的 实现将执行大量字符串复制,并且运行 时间的字符数为O(n^2)。更有表现力的人 方法是将结果累积到StringBuilder中, 它是用于累积字符串的可变容器。我们可以使用 和我们使用普通方法一样,使用同样的技术来并行可变约简 减少


因此,您应该改用StringBuilder。

对于刚开始使用lambdas和streams的人来说,花了相当长的时间才到达“啊哈”时刻,直到我真正理解这里发生了什么。对于像我这样的流媒体新手,我会重新表述一下,让它变得更容易一些(至少我希望它能得到真正的回答)

所有这些都在reduce文档中,其中说明:

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

我们可以很容易地证明,按照代码的方式,关联性被破坏了:

static private void isAssociative() {
     BinaryOperator<String> operator = (s1, s2) -> s1 + "/" + s2;
     String result = operator.apply("", "a");
     System.out.println(result); 
     System.out.println(result.equals("a")); 
}
static private void isAssociative(){
二进制运算符=(s1,s2)->s1+“/”+s2;
字符串结果=运算符。应用(“,“a”);
系统输出打印项次(结果);
System.out.println(result.equals(“a”));
}

一个空字符串与另一个字符串连接,应该真正产生第二个字符串;这不会发生,因此累加器(BinaryOperator)不是关联的,因此在并行调用的情况下reduce方法不能保证相同的结果。

事实上,如果您的
BinaryOperator
是,那么这应该适用于并行流。除了不遵循reduce规则之外,正如其他回答者所指出的,有一种更简单的方法来完成您正在做的事情:
stream.collect(加入(“/”)
最好使用条件表达式,IMHO:
(s1,s2)->s1.isEmpty()?s2:s2.isEmpty()?s1:s1+“/”+s2
此外,由于累加器函数不需要是可交换的,因此实际上不需要检查s2是否为空。那么,
(s1,s2)->s1.isEmpty()?s2:s1+“/”+s2
就足够了。或者他可以使用
stream.collect(收集器。加入(“/”,“/”,“))
所以最大的问题是,为什么实现不将
reduce(identity,acculator)
委托给
reduce(acculator)。或者将lse(identity)
内部作为“
acculator.apply(identity,t)的要求
等于
t
“意味着在每个计算线程中执行
whatever=accumulator.apply(identity,whatever)
都是胡说八道。@Holger可能只是因为它不适合FP纯粹主义者:)运算符的关联性没有被破坏,唯一的问题是identity值。看见
static private void isAssociative() {
     BinaryOperator<String> operator = (s1, s2) -> s1 + "/" + s2;
     String result = operator.apply("", "a");
     System.out.println(result); 
     System.out.println(result.equals("a")); 
}