nee*_*rle 2 java parallel-processing reduce java-8 java-stream
问题很简单:为什么我们不能在流中的操作中使用StringBuilder(...)as ,但可以用作?identity functionreduce(...)java8string1.concat(string2)identity function
string1.concat(string2)可以看作是相似的builder.append(string)(虽然可以理解为这些操作几乎没有区别),但是我无法理解reduce操作的区别。考虑以下示例:
List<String> list = Arrays.asList("1", "2", "3");
// Example using the string concatenation operation
System.out.println(list.stream().parallel()
.reduce("", (s1, s2) -> s1 + s2, (s1, s2)->s1 + s2));
// The same example, using the StringBuilder
System.out.println(list.stream() .parallel()
.reduce(new StringBuilder(""), (builder, s) -> builder
.append(s),(builder1, builder2) -> builder1
.append(builder2)));
// using the actual concat(...) method
System.out.println(list.stream().parallel()
.reduce("", (s1, s2) -> s1.concat(s2), (s1, s2)->s1.concat(s2)));
Run Code Online (Sandbox Code Playgroud)
这是执行上述行后的输出:
123
321321321321 // output when StringBuilder() is used as Identity
123
Run Code Online (Sandbox Code Playgroud)
builder.append(string)是一个关联操作str1.concat(str2)。那么为什么concat有效而append无效呢?
是的,append 是确实的关联,但这并不是由于储液器和组合传递函数的唯一要求。根据文档,它们必须是:
append不是无国籍的。它是有状态的。当你这样做sb.append("Hello"),它不仅返回一个StringBuilder与Hello追加到末尾,它也改变了内容的(即状态) sb。
也来自文档:
如果流操作的行为参数是有状态的,则流管道结果可能是不确定的或不正确的。有状态的 lambda(或其他实现适当功能接口的对象)的结果取决于在流管道执行期间可能发生变化的任何状态。
也正因为如此,new StringBuilder()一旦应用了累加器或组合器,就不是一个有效的身份。一些东西会被添加到空字符串构建器中,并且不再满足所有身份必须满足的以下等式:
combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
Run Code Online (Sandbox Code Playgroud)
并行流有可能在调用累加器和/或组合器后使用旧的字符串构建器,并期望它们的内容不被更改。但是,累加器和组合器会改变字符串构建器,导致流产生不正确的结果。
另一方面,concat满足上述所有三个。它是无状态的,因为它不会更改调用它的字符串。它只是重新调整一个新的连接字符串。(String无论如何都是不可变的,无法更改:D)
无论如何,这是一个可变减少的用例collect:
System.out.println((StringBuilder)list.stream().parallel()
.collect(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append
)
);
Run Code Online (Sandbox Code Playgroud)