【问题标题】:Java stream groupingBy and sum multiple fieldsJava流groupingBy并对多个字段求和
【发布时间】:2020-06-09 15:30:49
【问题描述】:

这是我的列表 fooList

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

    ... constructor, getters & setters
}

例如(帐户的所有值都已设置为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());
}

【讨论】:

  • 好像因为required: no arguments而无法做到return new Foo(incoming.getName(),incoming.getCode(),incoming.getAccount(), initial.getTime() + incoming.getTime());
  • @San.s 只需要一个适当的构造函数。
  • 我完全是 Java 初学者,你介意教我怎么做吗?谢谢你。我也对计算平均时间感到困惑。@Naman
  • @San.s 我所做的是遍历列表并根据少数属性找出相同的元素。接下来,我定义了一个合并函数aggregateTime,它将两个类似的对象合并为一个合并的最终对象(基于任何预期的逻辑)。如果您的期望也是平均时间,那么您将不得不一次迭代完整列表,然后评估找到匹配的值的平均值。但是,这与 summing 不同,后者不需要知道遍历的元素数量。
  • 我明白了..所以最好合并一个对象,它得到了分组namecode的数量,并求和了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。无论如何,这只是一个草稿
猜你喜欢
  • 2022-11-21
  • 1970-01-01
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 2019-02-03
  • 2016-12-29
  • 2017-09-28
  • 2018-03-30
相关资源
最近更新 更多