我将在这个练习中使用 jOOλ 库中的
Tuple2
类型,但如果你想避免依赖性,也可以创建自己的元组类型。
我还假设您正在使用此数据来表示:
class A {
final int w;
final int x;
final int y;
final int z;
A(int w, int x, int y, int z) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}
}
现在您可以编写:
Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics>> map =
Stream.of(
new A(1, 1, 1, 1),
new A(1, 2, 3, 1),
new A(9, 8, 6, 4),
new A(9, 9, 7, 4),
new A(2, 3, 4, 5),
new A(2, 4, 4, 5),
new A(2, 5, 5, 5))
.collect(Collectors.groupingBy(
a -> tuple(a.z, a.w),
Collector.of(
() -> tuple(new IntSummaryStatistics(), new IntSummaryStatistics()),
(r, t) -> {
r.v1.accept(t.x);
r.v2.accept(t.y);
},
(r1, r2) -> {
r1.v1.combine(r2.v1);
r1.v2.combine(r2.v2);
return r1;
}
)
));
甚至更好(使用最新的jOOλ API):
Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics>> map =
Seq.of(
new A(1, 1, 1, 1),
new A(1, 2, 3, 1),
new A(9, 8, 6, 4),
new A(9, 9, 7, 4),
new A(2, 3, 4, 5),
new A(2, 4, 4, 5),
new A(2, 5, 5, 5))
.groupBy(
a -> tuple(a.z, a.w),
Tuple.collectors(
Collectors.summarizingInt(a -> a.x),
Collectors.summarizingInt(a -> a.y)
)
);
地图结构现在是:
(z, w) -> (all_aggregations_of(x), all_aggregations_of(y))
调用上述地图的
toString()
将产生以下结果:
{
(1, 1) = (IntSummaryStatistics{count=2, sum=3, min=1, average=1.500000, max=2},
IntSummaryStatistics{count=2, sum=4, min=1, average=2.000000, max=3}),
(4, 9) = (IntSummaryStatistics{count=2, sum=17, min=8, average=8.500000, max=9},
IntSummaryStatistics{count=2, sum=13, min=6, average=6.500000, max=7}),
(5, 2) = (IntSummaryStatistics{count=3, sum=12, min=3, average=4.000000, max=5},
IntSummaryStatistics{count=3, sum=13, min=4, average=4.333333, max=5})
}
现在您已经获得了所有的统计数据。
附注
当然,我不知道您的确切要求,但我怀疑您很快就需要更复杂的聚合报告,例如中位数、反向分布和各种漂亮的OLAP特性,这时您会意识到SQL只是一种更容易处理这种任务的语言。
另一方面,我们肯定会添加更多类似SQL的功能到jOOλ。 这个主题也激发我写一篇更详细的博客文章来描述这种方法。
group by x, w
,你并不是“同时”分组,而只是使用元组(x, w)
作为键进行一次分组。在每个组内,所有条目的(x, w)
值都相同,因此每个组的min(x), max(x), avg(x)
将分别计算为x, x, x
。顺便说一句,如果你想要这样的聚合数据库操作,让数据库执行操作比手动获取整个表并进行聚合更有效率。 - Holger