Java 8 Lambda分组同时使用X和Y.

我正在寻找一个lambda来优化已检索的数据。 我有一个原始的结果集,如果用户没有更改我希望使用java的lambda按结果分组的日期。 我是java的lambdas的新手。

我正在寻找的lambda与这个查询相似。

select z, w, min(x), max(x), avg(x), min(y), max(y), avg(y) from table group by x, w; 

所以我假设您有一个对象列表,并且您想要创建一个具有给定分组的地图。 我对你的x,y,w,z有点困惑,所以我会用自己的字段。 但这是我要怎么做的:

 interface Entry { String getGroup1(); String getGroup2(); int getIntData(); double getDoubleData(); } List dataList; Map> groupedStats = dataList.stream() .collect(Collectors.groupingBy(Entry::getGroup1, Collectors.groupingBy(Entry::getGroup2, Collectors.summarizingInt(Entry::getIntData)))); 

然后,如果你想获得具有组A,B的项目的平均数据,那么你使用:

 groupedStats.get("A").get("B").getAverage(); 

如果要同时汇总多个数据集,则会更复杂一些。 您需要编写自己的包装类,可以累积多个统计信息。 这是一个示例,其中包含Entry中的两个数据项(我将它们设为int和double,以使其更有趣)。

 class CompoundStats { private final IntSummaryStatistics intDataStats = new IntSummaryStatistics(); private final DoubleSummaryStatistics doubleDataStats = new DoubleSummaryStatistics(); public void add(Entry entry) { intDataStats.accept(entry.getIntData()); doubleDataStats.accept(entry.getDoubleData()); } public CompoundStats combine(CompoundStats other) { intDataStats.combine(other.intDataStats); doubleDataStats.combine(other.doubleDataStats); return this; } } 

然后可以使用此类创建自己的收集器:

 Map> groupedStats = dataList.stream() .collect(Collectors.groupingBy(Entry::getGroup1, Collectors.groupingBy(Entry::getGroup2, Collector.of(CompoundStats::new, CompoundStats::add, CompoundStats::combine)))); 

现在你的地图返回一个CompoundStats而不是一个IntSummaryStatistics:

 groupedStats.get("A").get("B").getDoubleStats().getAverage(); 

另请注意,如果您创建了一个单独的类来保存您的分组而不是使用我上面提到的两步图,那么这将更整洁。 如果需要,再次不是一个困难的修改

希望这对你自己的情况很有用。

我将使用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> 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( // This is your GROUP BY criteria a -> tuple(az, aw), Collector.of( // When collecting, we'll aggregate data into two IntSummaryStatistics // for x and y () -> tuple(new IntSummaryStatistics(), new IntSummaryStatistics()), // The accumulator will simply take new t = (x, y) values (r, t) -> { r.v1.accept(tx); r.v2.accept(ty); }, // The combiner will merge two partial aggregations, // in case this is executed in parallel (r1, r2) -> { r1.v1.combine(r2.v1); r1.v2.combine(r2.v2); return r1; } ) )); 

甚至更好(使用最新的jOOλAPI):

 Map, Tuple2> map = // Seq is like a Stream, but sequential only, and with more features 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)) // Seq.groupBy() is just short for Stream.collect(Collectors.groupingBy(...)) .groupBy( a -> tuple(az, aw), // Because once you have tuples, why not add tuple-collectors? Tuple.collectors( Collectors.summarizingInt(a -> ax), Collectors.summarizingInt(a -> ay) ) ); 

地图结构现在是:

 (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}) } 

你现在收到了所有的统计数据。

边注

当然,我不知道您的确切要求,但我怀疑您将很快需要在报表中进行更复杂的聚合,例如中位数,逆分布以及各种不错的OLAPfunction,当您意识到SQL时对于这种任务来说,这只是一种更容易的语言。

另一方面,我们肯定会向jOOλ添加更多SQLesquefunction 。 这个主题也激发了我写一篇完整的博客文章,其中详细介绍了所描述的方法 。