有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

在Java8中,我当前的第一次尝试是这样的,我知道这个解决方案与Group by multiple field names in java 8中的这个响应类似

Map<Date, Map<String, Map<String, Map<String, Map<String, Integer>>>>>>> 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...

正如你所见,这是相当混乱和难以维护的

有人有更好的方法吗?任何帮助都将不胜感激

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


共 (1) 个答案

  1. # 1 楼答案

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

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

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