如何使用流操作来计算列表中的平均值,省略一些?

时间:2015-11-18 01:12:49

标签: java foreach java-8

我有一个方法可以返回多个模型对象的属性平均值:

List<Activity> activities = ...;
double effortSum = 0;
double effortCount = 0;
activities.stream().forEach(a -> {
    double effort = a.getEffort();
    if (effort != Activity.NULL) {
        effortCount++;            < Compilation error, local variable
        effortSum += effort;      < Compilation error, local variable
    }
});

但是,如上所述,上述尝试无法编译。我遇到的唯一解决方案是使用AtomicReferenceDouble对象,但这看起来很苛刻,并且为应该是一个简单的操作添加了大量的混淆。 (或者添加番石榴并获得AtomicDouble,但是得出了相同的结论。)

是否存在使用新Java 8循环修改局部变量的“最佳实践”策略?

活动的相关代码:

public class Activity {
    public static final double NULL = Double.MIN_VALUE;

    private double effort = NULL;

    public void setEffort(double effort) { this.effort = effort; }
    public double getEffort() { return this.effort; }

    ...
}

2 个答案:

答案 0 :(得分:6)

  

是否有最佳实践&#34;使用新的Java 8循环修改局部变量的策略?

是的:不要。你可以修改他们的属性 - 虽然它仍然是一个坏主意 - 但你不能自己修改它们;你只能引用lambda中的变量,如果它们是final或者可能是final。 (AtomicDouble确实是一个解决方案,另一个是double[1],只是作为持有者。)

实施&#34;平均值&#34;的正确方法这里的操作是

activities.stream()
    .mapToDouble(Activity::getEffort)
    .filter(effort -> effort != Activity.NULL)
    .average()
    .getAsDouble();

答案 1 :(得分:4)

在您的情况下,有一个功能更强大的解决方案 - 只需从流中计算摘要统计信息,您可以从中获取已过滤元素的数量及其总和:

DoubleSummaryStatistics stats = 
    activities.stream()
              .mapToDouble(Activity::getEffort)
              .filter(e -> e != Activity.NULL)
              .summaryStatistics();

long effortCount = stats.getCount();
double effortSum = stats.getSum();
  

是否存在修改局部变量的“最佳实践”策略   使用新的Java 8循环?

不要尝试这样做。我认为主要的问题是人们试图以强制性的方式使用新的Java 8功能翻译他们的代码(比如在你的问题中 - 然后你就会遇到麻烦!)。

首先尝试查看是否可以提供功能正常的解决方案(我相信这是Stream API的目标)。