Java 流分组依据和多个字段的总和

2022-09-02 03:25:05

我有一个列表 fooList

class Foo {
    private String category;
    private int amount;
    private int price;

    ... constructor, getters & setters
}

我想按类别分组,然后对金额和价格进行汇总。

结果将存储在地图中:

Map<Foo, List<Foo>> map = new HashMap<>();

关键是 Foo 保存汇总的金额和价格,并将列表作为具有相同类别的所有对象的值。

到目前为止,我已经尝试了以下内容:

Map<String, List<Foo>> map = fooList.stream().collect(groupingBy(Foo::getCategory()));

现在我只需要将 String 键替换为保存汇总金额和价格的 Foo 对象。这是我陷入困境的地方。我似乎找不到任何方法来做到这一点。


答案 1

有点丑陋,但它应该工作:

list.stream().collect(Collectors.groupingBy(Foo::getCategory))
    .entrySet().stream()
    .collect(Collectors.toMap(x -> {
        int sumAmount = x.getValue().stream().mapToInt(Foo::getAmount).sum();
        int sumPrice= x.getValue().stream().mapToInt(Foo::getPrice).sum();
        return new Foo(x.getKey(), sumAmount, sumPrice);
    }, Map.Entry::getValue));

答案 2

我对 Sweeper 答案的变体使用递减收集器而不是流式传输两次来求和各个字段:

Map<Foo, List<Foo>> map = fooList.stream()
    .collect(Collectors.groupingBy(Foo::getCategory))
    .entrySet().stream()
    .collect(Collectors.toMap(e -> e.getValue().stream()
        .collect(Collectors.reducing(
            (l, r) -> new Foo(l.getCategory(), l.getAmount() + r.getAmount(), l.getPrice() + r.getPrice())))
         .get(), e -> e.getValue()));

不过,它并不是更好,因为它创造了很多短暂的 。Foos

但请注意,这是提供 - 和 - 实现所必需的,这些实现仅考虑结果才能正常工作。这可能不是你想要的。我更喜欢定义一个单独的类来包含聚合数据。FoohashCodeequalscategorymapFooFooSummary