You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对含Duration的Person对象列表进行分组聚合与排序

按LocalDate和personType聚合Duration并排序的Java Stream实现

核心思路

要实现需求,关键完成三个步骤:

  1. 复合分组:同时按startDate和personType作为分组依据,确保同一日期同一类型的记录归为一组
  2. Duration聚合:对每组内的time1和time2分别调用Duration.plus()求和(Duration是不可变类,每次求和返回新实例)
  3. 结果排序:将聚合后的结果按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转换为小时:分钟的字符串格式,匹配示例输出要求

你原有代码的问题

  1. 仅按startDate分组,未考虑personType,导致同一日期不同类型的记录被混在一起
  2. 缺失归约逻辑,没有实现Duration的累加操作
  3. 最终返回的Map结构不符合需求,应直接生成聚合后的Person列表而非嵌套Map

内容的提问来源于stack exchange,提问作者Mc_Android

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 18:46:15