第三个参数 Java8 reduce on 对象中的字符串属性
Third param Java8 reduce on string attribute in Object
流大师的问题很简单:
我有这个:
List<MyBean> beans = new ArrayList<>();
beans.add(new MyBean("tutu"));
beans.add(new MyBean("toto"));
beans.add(new MyBean("titi"));
比较:
StringBuilder reduced
= beans.parallelStream()
.map(MyBean::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}, (left, right) -> left.append(right));
和
StringBuilder reduced
= beans.parallelStream()
.map(MyBean::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}/* WITHOUT THIRD PARAM*/);
为什么,第二个解决方案无法编译...第三个参数用于并行流...
你能解释一下为什么我无法编译第二个代码部分吗?
此行为是因为 reduce:
的重载
reduce(T identity, BinaryOperator<T> accumulator)
有两个参数,第二个是 BinaryOperator<T>
,它基本上表示对 两个相同类型的操作数 的操作产生与操作数相同类型的结果。在你的第二个代码片段中没有遵守这个合同,因为你的 map
操作 returns a Stream<String>
而 identity 是类型 StringBuilder
.为防止编译器错误,只需将 identity 值和 accumulator 函数的操作数设为相同类型即可,即:
StringBuilder reduced
= beans.stream()
.map(b -> new StringBuilder(b.getName()))
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}/* WITHOUT THIRD PARAM*/);
另一方面,reduce
的重载:
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
接受一个 BiFunction
作为第二个参数,它可以消耗 两种不同类型的对象 因此为什么你的第一个代码片段在没有编译器错误的情况下工作。
最后,不要忘记将 beans.parallelStream()
更改为 beans.stream()
,如上面的解决方案所示。
顺便说一句,请注意,在并行执行操作时,累加器以及组合器、必须是associative, non-interfering and stateless。如果不考虑这一点,您的结果将是 nondeterministic 任意结果。
编辑:
如Holger所述:
Modifying incoming parameters in a reduction function is broken by
definition, even if it happens to produce the intended outcome in a
sequential context. It also violates the contract of the first
parameter, as the modified StringBuilder
is not an identity value
anymore. You can use Reduction with immutable values, as
.map(MyBean::getName).reduce((a,b) -> a + ", " + b).orElse("");
or use
Mutable Reduction like
.map(MyBean::getName).collect(Collectors.joining(", "));
流大师的问题很简单:
我有这个:
List<MyBean> beans = new ArrayList<>();
beans.add(new MyBean("tutu"));
beans.add(new MyBean("toto"));
beans.add(new MyBean("titi"));
比较:
StringBuilder reduced
= beans.parallelStream()
.map(MyBean::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}, (left, right) -> left.append(right));
和
StringBuilder reduced
= beans.parallelStream()
.map(MyBean::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}/* WITHOUT THIRD PARAM*/);
为什么,第二个解决方案无法编译...第三个参数用于并行流...
你能解释一下为什么我无法编译第二个代码部分吗?
此行为是因为 reduce:
的重载reduce(T identity, BinaryOperator<T> accumulator)
有两个参数,第二个是 BinaryOperator<T>
,它基本上表示对 两个相同类型的操作数 的操作产生与操作数相同类型的结果。在你的第二个代码片段中没有遵守这个合同,因为你的 map
操作 returns a Stream<String>
而 identity 是类型 StringBuilder
.为防止编译器错误,只需将 identity 值和 accumulator 函数的操作数设为相同类型即可,即:
StringBuilder reduced
= beans.stream()
.map(b -> new StringBuilder(b.getName()))
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(name);
return builder;
}/* WITHOUT THIRD PARAM*/);
另一方面,reduce
的重载:
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
接受一个 BiFunction
作为第二个参数,它可以消耗 两种不同类型的对象 因此为什么你的第一个代码片段在没有编译器错误的情况下工作。
最后,不要忘记将 beans.parallelStream()
更改为 beans.stream()
,如上面的解决方案所示。
顺便说一句,请注意,在并行执行操作时,累加器以及组合器、必须是associative, non-interfering and stateless。如果不考虑这一点,您的结果将是 nondeterministic 任意结果。
编辑:
如Holger所述:
Modifying incoming parameters in a reduction function is broken by definition, even if it happens to produce the intended outcome in a sequential context. It also violates the contract of the first parameter, as the modified
StringBuilder
is not an identity value anymore. You can use Reduction with immutable values, as.map(MyBean::getName).reduce((a,b) -> a + ", " + b).orElse("");
or use Mutable Reduction like.map(MyBean::getName).collect(Collectors.joining(", "));