在Java 8中分组在具有聚合的多个字段上

我有一个与Web访问记录相关的域对象列表。 这些域对象可以扩展到数千个。

我没有资源或要求以原始格式将它们存储在数据库中,因此我希望预先计算聚合并将聚合数据放入数据库中。

我需要聚合在5分钟窗口中传输的总字节数, 下面的SQL查询

select round(request_timestamp, '5') as window, --round timestamp to the nearest 5 minute cdn, isp, http_result_code, transaction_time, sum(bytes_transferred) from web_records group by round(request_timestamp, '5'), cdn, isp, http_result_code, transaction_time 

在Java 8中我的第一个当前stab看起来像这样, 我知道这个解决方案类似于这个响应在Group 8中由多个字段名称在java 8中

 Map<Date, Map<String, Map<String, Map<String, Map>>>>>> aggregatedData = webRecords .stream() .collect(Collectors.groupingBy(WebRecord::getFiveMinuteWindow, Collectors.groupingBy(WebRecord::getCdn, Collectors.groupingBy(WebRecord::getIsp, Collectors.groupingBy(WebRecord::getResultCode, Collectors.groupingBy(WebRecord::getTxnTime, Collectors.reducing(0, WebRecord::getReqBytes(), Integer::sum))))))); 

这很有效,但它很难看,所有嵌套的地图都是噩梦! 要将地图“展平”或“展开”成行,我必须这样做

 for (Date window : aggregatedData.keySet()) { for (String cdn : aggregatedData.get(window).keySet()) { for (String isp : aggregatedData.get(window).get(cdn).keySet()) { for (String resultCode : aggregatedData.get(window).get(cdn).get(isp).keySet()) { for (String txnTime : aggregatedData.get(window).get(cdn).get(isp).get(resultCode).keySet()) { Integer bytesTransferred = aggregatedData.get(window).get(cdn).get(distId).get(isp).get(resultCode).get(txnTime); AggregatedRow row = new AggregatedRow(window, cdn, distId... 

正如您所看到的,这非常混乱且难以维护。

任何人都有更好的方法来做到这一点? 任何帮助将不胜感激。

我想知道是否有更好的方法来展开嵌套的地图,或者是否有一个允许你对集合进行GROUP BY的库。

您应该为地图创建自定义键。 最简单的方法是使用Arrays.asList

 Function> keyExtractor = wr -> Arrays.asList(wr.getFiveMinuteWindow(), wr.getCdn(), wr.getIsp(), wr.getResultCode(), wr.getTxnTime()); Map, Integer> aggregatedData = webRecords.stream().collect( Collectors.groupingBy(keyExtractor, Collectors.summingInt(WebRecord::getReqBytes))); 

在这种情况下,键是固定顺序的5个元素的列表。 不是面向对象,而是简单。 或者,您可以定义自己的类型,表示自定义键并创建正确的hashCode / equals实现。