【问题标题】:Java stream groupingBy and sum multiple fieldsJava流groupingBy和求和多个字段
【发布时间】:2022-11-21 05:38:40
【问题描述】:

这是我的列表 fooList

class Foo {
    private String name;
    private int code;
    private int account;
    private int time;
    private String others;

    ... constructor, getters & setters
}

例如(account的所有值都设置为1)

new Foo(First, 200, 1, 400, other1), 
new Foo(First, 200, 1, 300, other1),
new Foo(First, 201, 1, 10, other1),
new Foo(Second, 400, 1, 20, other2),
new Foo(Second, 400, 1, 40, other2),
new Foo(Third, 100, 1, 200, other3),
new Foo(Third, 101, 1, 900, other3)

我想通过对“名称”和“代码”进行分组、计算数量并对“时间”求和来转换这些值,例如

new Foo(First, 200, 2, 700, other1), 
new Foo(First, 201, 1, 10, other1),
new Foo(Second, 400, 2, 60, other2),
new Foo(Third, 100, 1, 200, other3),
new Foo(Third, 101, 1, 900, other3)

我知道我应该使用这样的流:

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

但是我怎样才能按代码对它们进行分组然后进行会计和汇总工作呢?


另外,如果我想计算平均时间怎么办?例如

new Foo(First, 200, 2, 350, other1), 
new Foo(First, 201, 1, 10, other1),
new Foo(Second, 400, 2, 30, other2),
new Foo(Third, 100, 1, 200, other3),
new Foo(Third, 101, 1, 900, other3)

我可以同时使用summingInt(Foo::getAccount)averagingInt(Foo::getTime) 吗?

【问题讨论】:

  • 为什么当你传递整数值时你的名字是字符串?
  • 也许我应该以更好的方式重命名它@MrFisherman

标签: java lambda java-stream grouping


【解决方案1】:

一种解决方法是处理 grouping,键为 List,并在映射回对象类型时进行强制转换。

List<Foo> result = fooList.stream()
        .collect(Collectors.groupingBy(foo ->
                        Arrays.asList(foo.getName(), foo.getCode(), foo.getAccount()),
                Collectors.summingInt(Foo::getTime)))
        .entrySet().stream()
        .map(entry -> new Foo((String) entry.getKey().get(0),
                (Integer) entry.getKey().get(1),
                entry.getValue(),
                (Integer) entry.getKey().get(2)))
        .collect(Collectors.toList());

更简洁的方法是公开用于合并功能的 API 并执行 toMap


编辑: toMap 的简化看起来像下面这样

List<Foo> result = new ArrayList<>(fooList.stream()
        .collect(Collectors.toMap(foo -> Arrays.asList(foo.getName(), foo.getCode()),
                Function.identity(), Foo::aggregateTime))
        .values());

其中 aggregateTimeFoo 中的静态方法,例如:

static Foo aggregateTime(Foo initial, Foo incoming) {
    return new Foo(incoming.getName(), incoming.getCode(),
            incoming.getAccount(), initial.getTime() + incoming.getTime());
}

【讨论】:

  • 看来我不能做return new Foo(incoming.getName(),incoming.getCode(),incoming.getAccount(), initial.getTime() + incoming.getTime());因为required: no arguments
  • @San.s 只需要一个合适的构造函数。
  • 我完全是 Java 的初学者,你介意教我怎么做吗?谢谢你。我对计算平均时间也有点困惑。@Naman
  • @San.s 我在那里所做的是遍历列表并根据几个属性找出相同的元素。接下来,我定义了一个合并函数aggregateTime,它将两个类似的对象和结果合并为一个合并的最终对象(基于任何预期的逻辑)。如果您的期望也是平均时间,那么您将不得不迭代一次完整列表,然后评估找到的匹配值的平均值。但是,这与不需要知道遍历的元素数量的 summing 不同。
  • 我明白了..所以最好合并一个有 groupednameandcode 和求和 time 的对象,然后进行一次除法以获得平均时间。我对吗?我也想知道什么是合适的构造函数好像。非常感谢! @纳曼
【解决方案2】:

您可以实现自己的收集机制,如下所示

        var collect = Stream.of(
                new Foo(1, 200, 1, 400),
                new Foo(1, 200, 1, 300),
                new Foo(1, 201, 1, 10),
                new Foo(2, 400, 1, 20),
                new Foo(2, 400, 1, 40),
                new Foo(3, 100, 1, 200),
                new Foo(3, 101, 1, 900)
        )
                .collect(
                        ArrayList::new,
                        (BiConsumer<ArrayList<Foo>, Foo>) (foos, foo) -> {
                            var newFoo = foos
                                    .stream()
                                    .filter(f -> f.name == foo.name && f.account == foo.account)
                                    .findFirst()
                                    .map(f -> new Foo(f.name, f.code, f.account + foo.account, f.time + foo.time))
                                    .orElse(foo);
                            foos.removeIf(f -> f.name == foo.name && f.account == foo.account);
                            foos.add(newFoo);
                        },
                        (foos, foos2) -> foos.addAll(foos2)
                );

【讨论】:

  • 我将 name 声明为 int。不管怎样,这只是一个草稿
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
相关资源
最近更新 更多