Java8计算地图中对象列表的平均值 [英] Java8 calculate average of list of objects in the map

查看:230
本文介绍了Java8计算地图中对象列表的平均值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

初始数据:

public class Stats {
    int passesNumber;
    int tacklesNumber;

    public Stats(int passesNumber, int tacklesNumber) {
        this.passesNumber = passesNumber;
        this.tacklesNumber = tacklesNumber;
    }

    public int getPassesNumber() {
        return passesNumber;
    }

    public void setPassesNumber(int passesNumber) {
        this.passesNumber = passesNumber;
    }

    public int getTacklesNumber() {
        return tacklesNumber;
    }

    public void setTacklesNumber(int tacklesNumber) {
        this.tacklesNumber = tacklesNumber;
    }
} 

Map<String, List<Stats>> statsByPosition = new HashMap<>();
statsByPosition.put("Defender", Arrays.asList(new Stats(10, 50), new Stats(15, 60), new Stats(12, 100)));
statsByPosition.put("Attacker", Arrays.asList(new Stats(80, 5), new Stats(90, 10)));

我需要按位置计算统计数据的平均值。因此结果应该是具有相同键的映射,但是值应该聚合到单个Stats对象(List应该简化为单个Stats对象)

I need to calculate an average of Stats by position. So result should be a map with the same keys, however values should be aggregated to single Stats object (List should be reduced to single Stats object)

{
  "Defender" => Stats((10 + 15 + 12) / 3, (50 + 60 + 100) / 3),
  "Attacker" => Stats((80 + 90) / 2, (5 + 10) / 2)
} 


推荐答案

我认为Java8中没有任何新功能可以真正帮助解决这个问题,至少效率不高。

I don't think there's anything new in Java8 that could really help in solving this problem, at least not efficiently.

如果仔细查看所有新API,那么您将看到它们中的大多数旨在提供更强大的原语来处理单个值及其序列 - 即,在 double序列 int ? extends Object 等。

If you look carefully at all new APIs, then you will see that majority of them are aimed at providing more powerful primitives for working on single values and their sequences - that is, on sequences of double, int, ? extends Object, etc.

例如,计算 double ,JDK引入了一个新类 - DoubleSummaryStatistics ,它做了一件显而易见的事情 - 收集任意序列 double 值的摘要。
我实际上建议你自己采取类似的方法:制作你自己的 StatsSummary 类,看起来如下:

For example, to compute an average on sequence on double, JDK introduces a new class - DoubleSummaryStatistics which does an obvious thing - collects a summary over arbitrary sequence of double values. I would actually suggest that you yourself go for similar approach: make your own StatsSummary class that would look along the lines of this:

// assuming this is what your Stats class look like:
class Stats {
  public final double a ,b; //the two stats
  public Stats(double a, double b) {
    this.a = a; this.b = b;
  }
}

// summary will go along the lines of:
class StatsSummary implements Consumer<Stats> {
  DoubleSummaryStatistics a, b; // summary of stats collected so far
  StatsSummary() {
    a = new DoubleSummaryStatistics();
    b = new DoubleSummaryStatistics();
  }

  // this is how we collect it:
  @Override public void accept(Stats stat) {
    a.accept(stat.a); b.accept(stat.b);
  }
  public void combine(StatsSummary other) {
    a.combine(other.a); b.combine(other.b);
  }

  // now for actual methods that return stuff. I will implement only average and min
  // but rest of them are not hard
  public Stats average() {
    return new Stats(a.getAverage(), b.getAverage());
  }
  public Stats min() {
    return new Stats(a.getMin(), b.getMin());
  }
}

现在,上面的实现实际上允许你表达你的使用 Stream 等时的正确意图:通过构建一个严格的API并使用JDK中可用的类作为构建块,您可以减少整体错误。

Now, above implementation will actually allow you to express your proper intents when using Streams and such: by building a rigid API and using classes available in JDK as building blocks, you get less errors overall.

但是,如果你只想在某个地方计算一次平均值并且不需要其他任何东西,那么编写这个类有点矫枉过正,这是一个快速而又肮脏的解决方案:

However, if you only want to compute average once somewhere and don't need anything else, coding this class is a little overkill, and here's a quick-and-dirty solution:

Map<String, Stats> computeAverage(Map<String, List<Stats>> statsByPosition) {
  Map<String, Stats> averaged = new HashMap<>();
  statsByPosition.forEach((position, statsList) -> {
    averaged.put(position, averageStats(statsList));
  });
  return averaged;
}

Stats averageStats(Collection<Stats> stats) {
  double a, b;
  int len = stats.size();
  for(Stats stat : stats) {
    a += stat.a;
    b += stat.b;
  }
  return len == 0d? new Stats(0,0) : new Stats(a/len, b/len);
}

这篇关于Java8计算地图中对象列表的平均值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆