【问题标题】:java 8 stream: grouping by and storing sum in new object, and merge mapsjava 8流:在新对象中分组和存储总和,并合并映射
【发布时间】:2017-08-15 12:47:54
【问题描述】:

我有一个类 Row,例如:

class Row {
   public Long id1;
   public String id2;
   public Long metric1;
   public Long metric2;

   public Stats getStats() {
      return new Stats(metric1, metric2);
   }
}

还有一个类 Stats:

class Stats{
    public Long totalMetric1;
    public Long totalMetric2;

    public void addMetric1(Long metric1) {
       this.totalMetric1 = this.totalMetric1 + metric1;
    }

    public void addMetric2(Long metric2) {
       this.totalMetric2 = this.totalMetric2 + metric2;
    }
}

我有一个行列表

List<Row> rowList;

我需要将其转换为按id1和id2分组的地图,并且我需要将度量数据以这种形式汇总到Stats对象中

Map<Long, Map<String, Stats>>

我正在使用 java 流来生成它,但停留在这一点:

Map<Long, Map<String, List<Stats>>> map = stream.collect(
            Collectors.groupingBy(r->r.id1(), 
            Collectors.groupingBy(r->r.id2,
            Collectors.mapping(r->r.getStats(), Collectors.toList()))));

如何将列表转换为另一个对象,该对象包含该列表中所有对象的总和?

还有没有办法使用 java 流将上述所需形式的两个输出映射合并到第三个?

例子:-

输入:行列表

<1,"ABC", 1, 2>
<1,"ABC", 2, 2>
<1,"XYZ", 1, 2>
<2,"ABC", 1, 2>
<2,"XYZ", 1, 2>
<3,"XYZ", 1, 0>
<3,"XYZ", 2, 1>
<3,"XYZ", 2, 3>

结果:按字段 1、字段 2 分组的地图,字段 3 和字段 4 的总和

1 - ABC - 3,4
    XYZ - 1,2
2 - ABC - 1,2
    XYZ - 1,2
3 - XYZ - 5,4

【问题讨论】:

  • 您希望有一个从RowMapMap,类型为StringStatsString 类型的东西是什么?
  • 你的Stats-constructor 在哪里接受两个参数?
  • 您真的想要一个以 id1 为键、值为以 id2 为键、然后以 stats 对象为值的附加映射的映射吗?或者拥有一个带有复合键和统计数据作为其值的映射是否也足够? (Map&lt;CompoundKey, Stats&gt;,其中CompoundKey 甚至可以是串联的String?)
  • @Harmlezz id2 是字符串类型,所以我想在第一个映射中按 id1 分组,然后按 id2 将该映射的值分组以获得第二个映射,id1 的类型为 Long 和 id2类型为String
  • @Roland 我正在寻找你首先描述的场景

标签: java lambda java-8 java-stream


【解决方案1】:

我的建议是使用比嵌套集合更简单的方法。在 Row 类中,添加

public Pair<Long,String> getIds() {
   return new Pair<>(id1,id2);
}

在 Stats 类中,添加

public Stats merge(Stats other) {
    return new Stats(totalMetric1+other.totalMetric1, totalMetric2 + other.totalMetric2);
}

然后写类似

      Map<Pair<Long, String>, Stats> stats = rowList.stream().
              collect(Collectors.toMap(Row::getIds,Row::getStats, (s1,s2) -> s1.merge(s2)));

如果您对番石榴不过敏(而且您不应该如此,这是每个项目中包含的最简单的库之一,至少对我来说是这样),您可以用更优雅和可读的方式编写它

      Table<Long, String, Stats> table = rowList.stream().
            collect(Tables.toTable(Row::getId1, Row::getId2, Row::getStats,(s1,s2) -> s1.merge(s2),HashBasedTable::create));

无需使用 Pair 或嵌套地图。

【讨论】:

  • 谢谢阿图尔,这对我有用!我有后续问题,如果我需要在Row 中按字段添加另一个组,Table 有没有办法支持这个?
  • 不,表格只是二维矩阵,我认为没有相同质量的 3 维或 n 维等效矩阵。然后,您需要在其中一个维度上开始使用元组(如 Pair)。 Table over Pair 的好处实际上是您可以轻松地在行或列上查询/迭代/等等。只要您不需要此类访问,您就可以继续使用复合键(我会为超过 2 个元素的任何内容创建显式类)。
猜你喜欢
  • 1970-01-01
  • 2019-10-03
  • 2022-01-23
  • 2020-08-10
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多