在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,流是顺序还是并行执行并不重要:
除了标识为显式非确定性的操作(例如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
这里有什么问题?
BTW:使用(首选).collect(Collectors.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
答案 0 :(得分:63)
来自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
不是必需的,因为累加器不一定是交换功能。
答案 1 :(得分:6)
要添加到其他答案,
您可能希望使用可变缩减,该文档指定执行类似
的操作String concatenated = strings.reduce("", String::concat)
会给出糟糕的表现结果。
我们会得到理想的结果,甚至可以并行工作。 但是,我们可能对性能不满意!这样的 实现会进行大量的字符串复制和运行 时间将是字符数O(n ^ 2)。性能更高 方法是将结果累积到 StringBuilder 中, 这是一个用于累积字符串的可变容器。我们可以使用 与普通人一样,将可变减少并行化的技术相同 还原
所以你应该使用StringBuilder。
答案 2 :(得分:2)
对于刚开始使用lambdas和溪流的人来说,花了很长时间才能到达AHA&#34;那一刻,直到我真的明白这里发生了什么。对于像我这样的流新手,我会对此进行一些改进,以使其更容易(至少我希望它能得到真正的回答)。
所有这些都在简化文档中指出:
标识值必须是累加器函数的标识。这意味着对于所有t,accumulator.apply(identity,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"));
}
与另一个String连接的空字符串应该真正产生第二个字符串;因为累加器(BinaryOperator)不是关联的,因此在并行调用的情况下,reduce方法不能保证相同的结果。