Java 使用reduce和collect查找平均值
我试图理解新的Java8流API 我找到了使用CollectAPI查找数字平均值的示例。但是我觉得,使用reduce()也可以做到这一点 1) 有什么理由我应该在这里使用collect而不是reduce吗?Java 使用reduce和collect查找平均值,java,lambda,functional-programming,java-8,java-stream,Java,Lambda,Functional Programming,Java 8,Java Stream,我试图理解新的Java8流API 我找到了使用CollectAPI查找数字平均值的示例。但是我觉得,使用reduce()也可以做到这一点 1) 有什么理由我应该在这里使用collect而不是reduce吗? 2) 如果我启用所有调试系统输出,我可以看到在、collect和reduce之间执行的操作完全相同。在这两种情况下,根本没有使用组合器。 3) 如果我使流并行,collect总是返回正确的结果。reduce()每次都给我不同的结果。 4) 我不应该在并行流中使用reduce吗 谢谢, Pa
2) 如果我启用所有调试系统输出,我可以看到在、collect和reduce之间执行的操作完全相同。在这两种情况下,根本没有使用组合器。
3) 如果我使流并行,collect总是返回正确的结果。reduce()每次都给我不同的结果。
4) 我不应该在并行流中使用reduce吗 谢谢,
Paulreduce和collect之间的区别在于,
collect
是一种增强的简化形式,可以并行处理可变对象。collect
算法线程限制各种结果对象,这样即使它们不是线程安全的,也可以安全地对它们进行变异。这就是为什么Averager
使用collect
工作的原因。对于使用reduce
的顺序计算,这通常并不重要,但是对于并行计算,正如您所观察到的,它将给出错误的结果
一个关键点是,reduce
只要处理的是值,而不是可变对象,它就可以工作。通过查看reduce
的第一个参数可以看到这一点。示例代码传递new Averager()
,它是一个单个对象,在并行简化中被多个线程用作标识值。并行流的工作方式是将工作负载分成由单个线程处理的段。如果多个线程正在变异同一个(非线程安全)对象,那么应该清楚为什么这会导致不正确的结果
可以使用reduce
计算平均值,但需要使累加对象不可变。考虑一个对象<代码> imutabalAlvase<代码>:
static class ImmutableAverager {
private final int total;
private final int count;
public ImmutableAverager() {
this.total = 0;
this.count = 0;
}
public ImmutableAverager(int total, int count) {
this.total = total;
this.count = count;
}
public double average() {
return count > 0 ? ((double) total) / count : 0;
}
public ImmutableAverager accept(int i) {
return new ImmutableAverager(total + i, count + 1);
}
public ImmutableAverager combine(ImmutableAverager other) {
return new ImmutableAverager(total + other.total, count + other.count);
}
}
请注意,我已经调整了accept
和combine
的签名,以返回一个新的ImmutableAverager
,而不是对该进行变异。(这些更改还使方法将函数参数与reduce
匹配,因此我们可以使用方法引用。)您可以像这样使用ImmutableAverager
:
double average = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.parallel()
.reduce(new ImmutableAverager(),
ImmutableAverager::accept,
ImmutableAverager::combine)
.average();
System.out.println("Average: "+average);
将不可变值对象与reduce
一起使用应该可以并行地给出正确的结果
最后,请注意,IntStream
和DoubleStream
有summaryStatistics()
方法,collector
有averagingdoull
、averagingingint
方法可以为您进行这些计算。然而,我认为问题更多的是收集和还原的机制,而不是如何最简洁地进行平均。感谢您提供如此详细的答案。一个小小的更正:收集实际上不是还原的专门化,相反。任何缩减都可以表示为一个集合,但是没有通用的方法将集合表示为缩减(或者至少没有方法不强制客户机代码管理并发性)。所以事实上,还原是一种特殊的收集形式。
double average = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.parallel()
.reduce(new ImmutableAverager(),
ImmutableAverager::accept,
ImmutableAverager::combine)
.average();
System.out.println("Average: "+average);