如何对含Duration的Person对象列表进行分组聚合与排序
按LocalDate和personType聚合Duration并排序的Java Stream实现
核心思路
要实现需求,关键完成三个步骤:
- 复合分组:同时按
startDate和personType作为分组依据,确保同一日期同一类型的记录归为一组 - Duration聚合:对每组内的
time1和time2分别调用Duration.plus()求和(Duration是不可变类,每次求和返回新实例) - 结果排序:将聚合后的结果按
startDate升序、personType升序排序
完整代码实现
1. 聚合与分组逻辑
直接用Collectors.toMap实现分组+合并,逻辑更简洁:
import java.time.LocalDate; import java.time.Duration; import java.util.AbstractMap; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; // 假设Person类已实现全参构造器与getter方法 class Person { private LocalDate startDate; private String personType; private Duration time1; private Duration time2; public Person(LocalDate startDate, String personType, Duration time1, Duration time2) { this.startDate = startDate; this.personType = personType; this.time1 = time1; this.time2 = time2; } // getter方法 public LocalDate getStartDate() { return startDate; } public String getPersonType() { return personType; } public Duration getTime1() { return time1; } public Duration getTime2() { return time2; } // 重写toString输出示例格式 @Override public String toString() { return String.format("(\"%s\",\"%s\",\"%s\",\"%s\")", startDate, personType, formatDuration(time1), formatDuration(time2)); } // Duration转小时:分钟格式的工具方法 private String formatDuration(Duration duration) { long hours = duration.toHours(); long minutes = duration.toMinutes() % 60; return String.format("%d:%02d", hours, minutes); } } // 业务逻辑实现 public class PersonAggregation { public static void main(String[] args) { List<Person> personList = // 初始化你的原始Person列表 // 分组聚合+排序 List<Person> aggregatedList = personList.stream() // 按日期+类型作为复合键分组,重复键时合并时间 .collect(Collectors.toMap( p -> new AbstractMap.SimpleEntry<>(p.getStartDate(), p.getPersonType()), p -> p, (existingPerson, newPerson) -> { // 累加time1和time2 Duration totalTime1 = existingPerson.getTime1().plus(newPerson.getTime1()); Duration totalTime2 = existingPerson.getTime2().plus(newPerson.getTime2()); return new Person(existingPerson.getStartDate(), existingPerson.getPersonType(), totalTime1, totalTime2); } )) .values().stream() // 按日期升序,再按类型升序排序 .sorted(Comparator.comparing(Person::getStartDate) .thenComparing(Person::getPersonType)) .collect(Collectors.toList()); // 打印结果(匹配示例格式) aggregatedList.forEach(System.out::println); } }
2. 代码细节解释
- 复合键设计:用
AbstractMap.SimpleEntry封装startDate和personType作为分组键,无需自定义实体类 - 合并逻辑:
toMap的第三个参数是合并函数,当同一键存在多条记录时,累加两组的time1和time2,返回新的聚合Person对象 - 排序规则:通过
Comparator.comparing().thenComparing()实现多字段排序,先按日期排序,再按类型排序 - Duration格式化:自定义
formatDuration方法将Duration转换为小时:分钟的字符串格式,匹配示例输出要求
你原有代码的问题
- 仅按
startDate分组,未考虑personType,导致同一日期不同类型的记录被混在一起 - 缺失归约逻辑,没有实现Duration的累加操作
- 最终返回的Map结构不符合需求,应直接生成聚合后的Person列表而非嵌套Map
内容的提问来源于stack exchange,提问作者Mc_Android
相关产品推荐
相关产品推荐

