Java实现:按日期和会话类型聚合消费与成本数据
实现方案
前提准备
先确认SessionDetail类的基础结构(需包含消费总和与成本总和的字段及构造/访问方法),示例如下:
class SessionDetail { private double totalConsumed; private double totalCost; public SessionDetail(double totalConsumed, double totalCost) { this.totalConsumed = totalConsumed; this.totalCost = totalCost; } // 可选:根据业务需求添加getter方法 public double getTotalConsumed() { return totalConsumed; } public double getTotalCost() { return totalCost; } }
方案1:直接从原始List<Result>生成目标嵌套Map
无需先生成dailyResults,通过Stream一步完成双层分组+求和:
Map<String, Map<String, SessionDetail>> targetMap = results.stream() .collect(Collectors.groupingBy( Result::getSessionDate, // 外层按sessionDate分组 Collectors.groupingBy( // 内层按SESSION_TYPE的字符串形式作为键 res -> res.getSESSION_TYPE().name(), // 将同类型的Result聚合为SessionDetail Collectors.reducing( new SessionDetail(0.0, 0.0), // 初始累加值 // 将单个Result转换为SessionDetail res -> new SessionDetail(res.getConsumed(), res.getCost()), // 累加逻辑:合并两个SessionDetail的数值 (detail1, detail2) -> new SessionDetail( detail1.getTotalConsumed() + detail2.getTotalConsumed(), detail1.getTotalCost() + detail2.getTotalCost() ) ) ) ));
优势:仅遍历原始数据集一次,性能更优,代码紧凑。
方案2:基于已有的dailyResults生成目标嵌套Map
如果已经提前得到了按日期分组的dailyResults,可以基于它做二次处理:
方式A:循环遍历处理
Map<String, Map<String, SessionDetail>> targetMap = new HashMap<>(); for (Map.Entry<String, List<Result>> dailyEntry : dailyResults.entrySet()) { String date = dailyEntry.getKey(); List<Result> dailyList = dailyEntry.getValue(); // 处理单日数据:按SESSION_TYPE分组求和 Map<String, SessionDetail> typeDetailMap = dailyList.stream() .collect(Collectors.groupingBy( res -> res.getSESSION_TYPE().name(), Collectors.collectingAndThen( Collectors.toList(), list -> { // 一次性计算两个字段的总和 double sumConsumed = list.stream().mapToDouble(Result::getConsumed).sum(); double sumCost = list.stream().mapToDouble(Result::getCost).sum(); return new SessionDetail(sumConsumed, sumCost); } ) )); targetMap.put(date, typeDetailMap); }
方式B:流式处理dailyResults
Map<String, Map<String, SessionDetail>> targetMap = dailyResults.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> entry.getValue().stream() .collect(Collectors.groupingBy( res -> res.getSESSION_TYPE().name(), Collectors.reducing( new SessionDetail(0.0, 0.0), res -> new SessionDetail(res.getConsumed(), res.getCost()), (d1, d2) -> new SessionDetail(d1.getTotalConsumed() + d2.getTotalConsumed(), d1.getTotalCost() + d2.getTotalCost()) ) )) ));
关键说明
- 内层分组键使用
SESSION_TYPE.name()将枚举转换为字符串,符合目标Map的String类型键要求; - 若追求性能,优先使用
Collectors.reducing(仅遍历分组内数据一次),而非先转List再两次求和; - 若
SessionDetail提供了merge方法,可简化累加逻辑,例如:// 在SessionDetail中添加merge方法 public void merge(SessionDetail other) { this.totalConsumed += other.getTotalConsumed(); this.totalCost += other.getTotalCost(); } // 此时reducing的累加逻辑可改为 (detail1, detail2) -> { detail1.merge(detail2); return detail1; }
内容的提问来源于stack exchange,提问作者Howard B
相关产品推荐
相关产品推荐

