首页 > 解决方案 > Java流groupingBy并对多个字段求和

问题描述

这是我的列表 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)代替吗?

标签: javalambdajava-streamgrouping

解决方案


一种解决方法可能是在映射回对象类型时处理groupingkey as和强制转换。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());
}

推荐阅读