我已阅读以下内容:
https://stackoverflow.com/a/22814174/2674303
我决定组合器仅在并行流中使用以获得正确的合并累加器结果。每个线程上有一个累加器实例。
因此我决定在没有合并器的情况下减少将无法正常工作。
为了检查这个,我写了以下例子:
Person reduce = Person.getPersons().stream()
.parallel()
.reduce(new Person(), (intermediateResult, p2) -> {
System.out.println(Thread.currentThread().getName());
return new Person("default", intermediateResult.getAge() + p2.getAge());
});
System.out.println(reduce);
模型:
public class Person {
String name;
Integer age;
///...
public static Collection<Person> getPersons() {
List<Person> persons = new ArrayList<>();
persons.add(new Person("Vasya", 12));
persons.add(new Person("Petya", 32));
persons.add(new Person("Serj", 10));
persons.add(new Person("Onotole", 18));
return persons;
}
}
如你所见,我不提供组合器
样本输出:
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
Person{name='default', age=72}
我已经多次执行了应用程序,并且总能看到正确的结果。
请解释如果没有提供合并器,如何减少并行流的工作。
答案 0 :(得分:6)
在这种情况下,您的累加器也可以用作组合器。这是缩减类型与流元素类型相同时的简写。因此
myStream.reduce(identity, accumulator);
完全等同于
myStream.reduce(identity, accumulator, accumulator);
您甚至可以在OpenJDK中查看这些方法的源代码:
@Override
public final <R> R reduce(R identity, BiFunction<R, ? super P_OUT, R> accumulator,
BinaryOperator<R> combiner) {
return evaluate(ReduceOps.makeRef(identity, accumulator, combiner));
}
@Override
public final P_OUT reduce(final P_OUT identity, final BinaryOperator<P_OUT> accumulator) {
return evaluate(ReduceOps.makeRef(identity, accumulator, accumulator));
}
三参数版本更灵活,因为缩减操作可能产生另一种类型的对象。在这种情况下,您不能使用双参数缩减,因为您没有提供如何组合结果类型的两个元素的规则。但是当结果类型相同时,累加器和组合器在同一个对象类型上工作,因此如果它是关联的,它应该是相同的操作。
答案 1 :(得分:4)
你已经指定了一个组合器。在这种情况下,组合器功能与累加器功能相同。
如果结果类型与您的流元素类型相同,则始终可以这样做。
通过总结值与减少进行比较,a+b+c+d
可以通过计算(a+b)+(c+d)
并行评估。这里,累加器是加法,它与处理(a+b)
和(c+d)
的中间结果的组合函数相同。
这个例子还表明,除非涉及类型转换,否则如果你需要一个不同的组合器函数会很奇怪,因为累加器函数的关联约束意味着它通常足以作为组合器函数。请注意,无论流是否计算a+b+c+d
,(a+b+c)+d
,(a+b)+(c+d)
或a+(b+c+d)
,都应该无关紧要。
答案 2 :(得分:0)
3-argument reduce
存在一种看似如下的不常见的情况:
想象一下,您没有将Person
的流简化为Person
,而是使用了不同的中间值,例如PopulationStats
。
class PopulationStats {
// make new stats that includes this person
PopulationStats addPerson(Person p) {
return new PopulationStats(........);
}
// make new stats that combines this and other stats
PopulationStats addStats(PopulationStats other) {
return new PopulationStats(........);
}
}
在这种情况下,3参数reduce
用于避免在减少之前为每个PopulationStats
制作Person
的中间步骤。
PopulationStats stats = people.stream()
.reduce(new PopulationStats(), PopulationStats::addPerson, PopulationStats::addStats);