java - 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)
代替吗?
解决方案
一种解决方法可能是在映射回对象类型时处理grouping
key 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());
其中aggregateTime
是Foo
这样的静态方法:
static Foo aggregateTime(Foo initial, Foo incoming) {
return new Foo(incoming.getName(), incoming.getCode(),
incoming.getAccount(), initial.getTime() + incoming.getTime());
}