首页 > Java > java教程 > 正文

Java中基于共享值聚合对象并提取首尾日期教程

聖光之護
发布: 2025-09-14 10:21:00
原创
790人浏览过

Java中基于共享值聚合对象并提取首尾日期教程

本教程详细介绍了如何使用Java Stream API高效地将列表中的实体对象按共享的组号(GroupNum)进行聚合。通过Collectors.groupingBy实现分组,然后对每个组提取其最早的开始日期(Start Date)和最晚的结束日期(Stop Date),最终生成一个汇总后的实体列表,极大地简化了数据处理逻辑。

1. 场景描述与需求分析

在数据处理中,我们经常遇到需要对具有共同属性的对象进行聚合的场景。例如,给定一个entities列表,其中每个实体都包含一个开始日期(start_dt)、一个结束日期(stop_dt)和一个组号(groupnum)。如果多个实体拥有相同的groupnum,则它们属于同一个逻辑组。我们的目标是聚合这些实体,对于每个组,提取该组中所有实体的最早start_dt和最晚stop_dt,并保留其groupnum,生成一个新的汇总实体列表。

考虑以下原始数据示例:

Start Stop GroupNum
2018-11-13 2019-01-13 1
2019-01-14 2019-03-06 1
2019-03-07 2019-11-18 1
2020-08-23 2020-08-23 2
2021-11-19 2022-12-23 2

期望的聚合结果应为:

Start Stop GroupNum
2018-11-13 2019-11-18 1
2020-08-23 2022-12-23 2

可以看到,对于GroupNum=1的实体,其最早的start_dt是2018-11-13,最晚的stop_dt是2019-11-18。对于GroupNum=2的实体,其最早的start_dt是2020-08-23,最晚的stop_dt是2022-12-23。

2. 实体类定义

首先,我们需要定义Entities类。为了更好地处理日期,推荐使用Java 8引入的java.time包中的LocalDate类,它提供了更强大和易用的日期操作功能。

立即学习Java免费学习笔记(深入)”;

WeShop唯象
WeShop唯象

WeShop唯象是国内首款AI商拍工具,专注电商产品图片的智能生成。

WeShop唯象 113
查看详情 WeShop唯象
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

public class Entities {
    private final LocalDate start_dt;
    private final LocalDate stop_dt;
    private int groupNum;

    // 日期格式化器,用于从字符串解析日期
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public Entities(String start_dt_str, String stop_dt_str, int groupNum) {
        this.start_dt = LocalDate.parse(start_dt_str, FORMATTER);
        this.stop_dt = LocalDate.parse(stop_dt_str, FORMATTER);
        this.groupNum = groupNum;
    }

    public Entities(LocalDate start_dt, LocalDate stop_dt, int groupNum) {
        this.start_dt = start_dt;
        this.stop_dt = stop_dt;
        this.groupNum = groupNum;
    }

    // Getters
    public LocalDate getStart_dt() {
        return start_dt;
    }

    public LocalDate getStop_dt() {
        return stop_dt;
    }

    public int getGroupNum() {
        return groupNum;
    }

    // toString 方法方便打印输出
    @Override
    public String toString() {
        return "Entities [start_dt=" + start_dt.format(FORMATTER) + 
               ", stop_dt=" + stop_dt.format(FORMATTER) + 
               ", groupNum=" + groupNum + "]";
    }

    // hashCode 和 equals 方法,如果需要将Entities对象作为Map的键,或者进行集合比较
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Entities entities = (Entities) o;
        return groupNum == entities.groupNum &&
               Objects.equals(start_dt, entities.start_dt) &&
               Objects.equals(stop_dt, entities.stop_dt);
    }

    @Override
    public int hashCode() {
        return Objects.hash(start_dt, stop_dt, groupNum);
    }
}
登录后复制

3. 使用Java Stream API进行聚合

Java 8的Stream API为这类数据转换提供了强大而简洁的解决方案。我们将利用Collectors.groupingBy将实体按groupNum分组,然后对每个组进行映射,提取所需的首尾日期。

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EntityAggregator {

    public static void main(String[] args) {
        // 准备原始数据
        List<Entities> baseList = new ArrayList<>();
        try {
            baseList.add(new Entities("2018-11-13", "2019-01-13", 1));
            baseList.add(new Entities("2019-01-14", "2019-03-06", 1));
            baseList.add(new Entities("2019-03-07", "2019-11-18", 1));
            baseList.add(new Entities("2020-08-23", "2020-08-23", 2));
            baseList.add(new Entities("2021-11-19", "2022-12-23", 2));
        } catch (DateTimeParseException e) {
            System.err.println("日期解析错误: " + e.getMessage());
            return;
        }

        // 执行聚合操作
        List<Entities> result = aggregateEntitiesByGroup(baseList);

        // 打印结果
        result.forEach(System.out::println);
    }

    /**
     * 根据groupNum聚合实体,提取每个组的最早开始日期和最晚结束日期。
     *
     * @param entitiesList 原始实体列表
     * @return 聚合后的实体列表
     */
    public static List<Entities> aggregateEntitiesByGroup(List<Entities> entitiesList) {
        if (entitiesList == null || entitiesList.isEmpty()) {
            return new ArrayList<>();
        }

        return entitiesList.stream()
            // 1. 按 groupNum 分组,得到 Map<Integer, List<Entities>>
            .collect(Collectors.groupingBy(Entities::getGroupNum))
            // 2. 将 Map 的 EntrySet 转换为 Stream
            .entrySet().stream()
            // 3. 对每个 Entry (即每个组) 进行映射,生成一个新的 Entities 对象
            .map(entry -> {
                Integer groupNum = entry.getKey(); // 获取组号
                List<Entities> groupEntities = entry.getValue(); // 获取该组的所有实体

                // 确保组内实体不为空,虽然 groupingBy 保证了这一点
                if (groupEntities.isEmpty()) {
                    return null; // 或者抛出异常,取决于业务需求
                }

                // 找到组内最早的 start_dt
                LocalDate firstStartDate = groupEntities.stream()
                                                      .map(Entities::getStart_dt)
                                                      .min(LocalDate::compareTo)
                                                      .orElse(null); // 如果列表为空,则返回null

                // 找到组内最晚的 stop_dt
                LocalDate lastStopDate = groupEntities.stream()
                                                     .map(Entities::getStop_dt)
                                                     .max(LocalDate::compareTo)
                                                     .orElse(null); // 如果列表为空,则返回null

                // 创建并返回新的聚合实体
                return new Entities(firstStartDate, lastStopDate, groupNum);
            })
            // 4. 过滤掉可能产生的null值(如果上述逻辑中返回了null)
            .filter(Objects::nonNull)
            // 5. 将结果收集为 List
            .collect(Collectors.toList());
    }
}
登录后复制

3.1 代码解析

  1. entitiesList.stream(): 将原始List<Entities>转换为一个流,以便进行链式操作。
  2. .collect(Collectors.groupingBy(Entities::getGroupNum)): 这是核心步骤。它根据Entities对象的groupNum属性对流中的元素进行分组。结果是一个Map<Integer, List<Entities>>,其中键是groupNum,值是属于该groupNum的所有Entities对象的列表。
  3. .entrySet().stream(): 获取上一步生成的Map的EntrySet,并将其转换为一个新的流。现在流中的每个元素都是一个Map.Entry<Integer, List<Entities>>,代表一个独立的组。
  4. .map(entry -> { ... }): 对流中的每个Map.Entry(即每个组)应用一个映射函数。在这个函数内部:
    • 我们提取groupNum(entry.getKey())和该组的Entities列表(entry.getValue())。
    • groupEntities.stream().map(Entities::getStart_dt).min(LocalDate::compareTo).orElse(null): 这是一个子流操作,用于查找组内最早的start_dt。
      • groupEntities.stream(): 将当前组的实体列表转换为流。
      • map(Entities::getStart_dt): 将实体流转换为LocalDate流,只包含start_dt。
      • min(LocalDate::compareTo): 查找此LocalDate流中的最小值(即最早日期)。
      • orElse(null): 如果流为空(理论上不会发生,因为groupingBy会保证组内有元素),则返回null。
    • groupEntities.stream().map(Entities::getStop_dt).max(LocalDate::compareTo).orElse(null): 类似地,用于查找组内最晚的stop_dt。
      • max(LocalDate::compareTo): 查找此LocalDate流中的最大值(即最晚日期)。
    • 最后,使用找到的最早start_dt、最晚stop_dt和组号创建一个新的Entities对象并返回。
  5. .filter(Objects::nonNull): 如果在map操作中因为某些异常情况(例如orElse返回了null)导致生成了null实体,此步骤会将其过滤掉。
  6. .collect(Collectors.toList()): 将所有新生成的聚合实体收集到一个新的List<Entities>中,作为最终结果。

4. 运行结果

执行上述main方法,将得到以下输出:

Entities [start_dt=2018-11-13, stop_dt=2019-11-18, groupNum=1]
Entities [start_dt=2020-08-23, stop_dt=2022-12-23, groupNum=2]
登录后复制

这与我们预期的聚合结果完全一致。

5. 注意事项与最佳实践

  • 日期类型选择: 在Java 8及更高版本中,强烈推荐使用java.time包下的日期时间API(如LocalDate, LocalDateTime, ZonedDateTime)而非传统的java.util.Date和java.util.Calendar。java.time API是线程安全的、不可变的,并提供了更直观和功能丰富的日期时间操作。
  • 空列表处理: 在aggregateEntitiesByGroup方法中,我们添加了对输入entitiesList为空或null的检查,以避免NullPointerException并返回一个空的列表。
  • 性能考量: 对于非常大的数据集,Stream API通常表现良好,因为它能够利用多核处理器进行并行处理(通过.parallelStream())。然而,对于极端情况,或者如果聚合逻辑非常复杂,可能需要进行性能基准测试。
  • 错误处理: 在main方法中,我们添加了try-catch块来捕获DateTimeParseException,以防日期字符串格式不正确。在实际应用中,应根据业务需求进行更完善的错误处理。
  • 可读性: 尽管Stream API非常强大,但过度复杂的链式操作可能会降低代码的可读性。对于非常复杂的聚合逻辑,可以考虑将其分解为多个辅助方法,或者在某些情况下,传统的循环可能更易于理解和调试。

6. 总结

本教程展示了如何利用Java Stream API的Collectors.groupingBy结合map操作,优雅且高效地解决基于共享属性聚合对象并提取特定字段的需求。通过将复杂的数据转换逻辑表达为一系列清晰的、声明性的操作,Stream API不仅提高了代码的简洁性,也增强了其可读性和可维护性。掌握这种模式对于现代Java开发中处理集合数据至关重要。

以上就是Java中基于共享值聚合对象并提取首尾日期教程的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号