使用Java Stream高效处理列表去重:按ID保留最新记录

使用java stream高效处理列表去重:按id保留最新记录

本教程详细介绍了如何利用Java Stream API,特别是Collectors.toMap的三参数重载方法,对包含重复ID的对象列表进行去重操作。通过结合Function.identity()和BinaryOperator.maxBy与自定义比较器,可以优雅地解决在ID重复时保留具有最新时间戳记录的需求,最终将处理后的唯一对象集合转换为列表,并可选择进行排序。

在日常的Java开发中,我们经常会遇到需要处理包含重复数据的列表。一个常见的场景是,列表中存在多个对象拥有相同的唯一标识符(如ID),但我们希望根据某个时间字段(如创建时间、更新时间)来决定保留哪一个记录,通常是保留最新的一条。本文将深入探讨如何使用Java Stream API以一种简洁高效的方式实现这一目标。

问题场景与解决方案概述

假设我们有一个Student对象列表,每个Student对象包含一个id和一个startDatetime。我们的目标是:如果存在多个Student对象拥有相同的id,则只保留其中startDatetime最新的那一个。

Java Stream API为我们提供了强大的工具来处理集合数据。解决此问题的核心在于使用Collectors.toMap的重载版本,它允许我们定义一个合并函数来处理键冲突。

定义数据模型

首先,我们需要一个表示学生的数据模型。为了演示,我们定义一个简单的Student类:

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

表单大师AI 表单大师AI

一款基于自然语言处理技术的智能在线表单创建工具,可以帮助用户快速、高效地生成各类专业表单。

表单大师AI 74 查看详情 表单大师AI

import java.time.LocalDateTime;import java.util.Objects; // 引入Objects用于hashCode和equalspublic class Student {    private String id;    private LocalDateTime startDatetime;    public Student(String id, LocalDateTime startDatetime) {        this.id = id;        this.startDatetime = startDatetime;    }    public String getId() {        return id;    }    public LocalDateTime getStartDatetime() {        return startDatetime;    }    // 为了更好的演示和调试,建议重写toString方法    @Override    public String toString() {        return "Student{" +               "id='" + id + ''' +               ", startDatetime=" + startDatetime +               '}';    }    // 重写equals和hashCode方法,虽然在此特定场景不直接用于去重,但良好的实践    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Student student = (Student) o;        return Objects.equals(id, student.id) && Objects.equals(startDatetime, student.startDatetime);    }    @Override    public int hashCode() {        return Objects.hash(id, startDatetime);    }}

使用 Collectors.toMap 进行去重

Collectors.toMap有多个重载方法,其中一个接受三个参数:keyMapper、valueMapper和mergeFunction。这个mergeFunction是解决我们问题的关键。

keyMapper: 定义如何从流中的元素提取键。在这里,我们将使用Student::getId作为键,因为我们希望根据id进行去重。valueMapper: 定义如何从流中的元素提取值。由于我们希望保留整个Student对象,所以这里可以使用Function.identity(),它表示将流中的元素本身作为值。mergeFunction: 这是处理键冲突(即当两个或更多元素生成相同的键时)的函数。它的签名是BinaryOperator,接收两个相同类型的参数并返回一个相同类型的结果。我们的目标是保留startDatetime最新的学生对象。

为了实现mergeFunction,我们可以使用BinaryOperator.maxBy方法,它接受一个Comparator作为参数,并返回一个BinaryOperator,该操作符会根据比较器选择两个元素中“最大”的一个。结合Comparator.comparing,我们可以轻松地比较Student对象的startDatetime。

示例代码

import java.time.LocalDateTime;import java.util.ArrayList;import java.util.Comparator;import java.util.List;import java.util.Map;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.stream.Collectors;public class StudentDeduplicator {    public static void main(String[] args) {        // 示例数据        List students = new ArrayList(List.of(            new Student("1", LocalDateTime.now()), // 最新的id为1的学生            new Student("1", LocalDateTime.of(2000, 2, 1, 1, 1)),            new Student("1", LocalDateTime.of(1990, 2, 1, 1, 1)),            new Student("2", LocalDateTime.of(1990, 2, 1, 1, 1)),            new Student("3", LocalDateTime.of(2020, 5, 10, 10, 0)),            new Student("3", LocalDateTime.of(2021, 1, 1, 0, 0)) // 最新的id为3的学生        ));        System.out.println("原始学生列表:");        students.forEach(System.out::println);        System.out.println("--------------------");        // 使用Stream进行去重,保留最新记录        List uniqueStudents = students.stream()            .collect(Collectors.toMap(                Student::getId, // KeyMapper: 使用Student的ID作为Map的键                Function.identity(), // ValueMapper: 将整个Student对象作为Map的值                BinaryOperator.maxBy(Comparator.comparing(Student::getStartDatetime)) // MergeFunction: 当ID冲突时,保留startDatetime最大的那个            ))            .values() // 获取Map中所有的值(即去重后的Student对象)            .stream() // 将值集合转换为新的Stream            // 结果通常需要按某个顺序排列,例如按startDatetime排序            .sorted(Comparator.comparing(Student::getStartDatetime))            .toList(); // Java 16+ 使用.toList(),Java 8-15使用.collect(Collectors.toList())        System.out.println("去重并排序后的学生列表:");        uniqueStudents.forEach(System.out::println);    }}

代码解析

students.stream(): 创建一个Student对象的流。.collect(Collectors.toMap(…)): 这是核心操作。Student::getId: 定义了Map的键,即学生的ID。Function.identity(): 定义了Map的值,即Student对象本身。BinaryOperator.maxBy(Comparator.comparing(Student::getStartDatetime)): 这是合并函数。当遇到重复的ID时,maxBy会使用提供的Comparator来比较两个Student对象。Comparator.comparing(Student::getStartDatetime)创建了一个比较器,它根据startDatetime字段进行比较。maxBy会选择startDatetime“更大”(即更晚)的那个Student对象保留下来。.values(): Collectors.toMap的结果是一个Map。.values()方法返回Map中所有值的集合(Collection),这些值就是我们去重后保留的Student对象。.stream(): 将Collection再次转换为一个流,以便后续操作。.sorted(Comparator.comparing(Student::getStartDatetime)): 这一步是可选的,但通常为了输出结果的可预测性,我们会对最终的列表进行排序。这里我们再次按照startDatetime进行升序排序。.toList(): 将流中的元素收集到一个不可变的List中。对于Java 8到Java 15,需要使用Collectors.toList()。

注意事项

Function.identity(): 这个静态方法返回一个简单的函数,它总是返回其输入参数。在这里,它确保整个Student对象被用作Map的值。BinaryOperator.maxBy vs BinaryOperator.minBy: 如果你的需求是保留startDatetime最“旧”的记录,那么应该使用BinaryOperator.minBy。Java 版本兼容性:List.of(…)用于创建不可变列表(Java 9+)。.toList()是Java 16+的新特性,用于将Stream收集为不可变列表。在Java 8到Java 15中,你需要使用collect(Collectors.toList())。性能考量: 这种方法涉及到构建一个中间的Map。对于非常大的数据集,这会占用额外的内存。然而,对于大多数常见场景,这种方法既简洁又高效。线程安全性: Stream操作本身是无状态的,但如果Student对象是可变的,并且在流操作之外被修改,可能会导致意外行为。在这种场景下,Student对象通常被视为不可变或在流处理过程中不被修改。

总结

通过巧妙地运用Java Stream API中的Collectors.toMap和BinaryOperator.maxBy,我们可以以声明式且易于理解的方式解决列表去重并保留最新记录的问题。这种模式不仅适用于时间戳,也可以应用于任何需要根据某个属性进行比较并保留“最佳”记录的场景,极大地提高了代码的可读性和简洁性。掌握这种Stream操作模式,将有助于你更高效地处理Java集合数据。

以上就是使用Java Stream高效处理列表去重:按ID保留最新记录的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/232314.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Win10如何将txt转换成pdf格式
上一篇 2025年11月3日 21:39:32
当书网最新域名入口_当书downbook官方登录地址
下一篇 2025年11月3日 21:39:36

相关推荐

  • ​​VSCode高手才知道的骚操作!学会这些技巧开发快人一步​​

    掌握VSCode效率核心在于命令面板、自定义快捷键、多光标编辑、代码片段与扩展生态;通过减少鼠标依赖、实现快速跳转与自动化操作,构建专属高效开发环境,让注意力聚焦于代码思维而非工具操作。 VSCode里那些让你效率翻倍的“骚操作”,本质上是将开发流程中的重复性、高频操作进行极致的简化与自动化。它不是…

    2026年9月22日
    300
  • 工信部批复:eSIM 手机业务全网开通,暂不支持线上方式

    10 月 14 日消息,据 c114 通讯网报道,中国电信、中国联通与中国移动已于今日正式获得批准,可开展 esim 手机运营服务的商用试验。 根据三大运营商公布的相关信息,eSIM 手机服务将覆盖全国 31 个省、自治区及直辖市,并正式进入市场销售阶段。 需要注意的是,在此次商用试验阶段,暂不支持…

    2026年9月22日
    000
  • CanvaPro中AI生成图片如何导出为PDF?快速保存图像的方法

    在Canva Pro中导出AI生成图片为PDF,需先将图片添加至设计,点击“分享”→“下载”→选择“PDF标准”或“PDF打印”即可。2. PDF标准适用于在线分享,文件小、加载快;PDF打印适用于高质量印刷,支持300 DPI和CMYK色彩模式,确保色彩准确与细节清晰。3. 为保证AI图片导出质量…

    2026年9月22日
    100
  • 怎样在iPhone情侣模式中管理消息通知?自定义提示音的技巧

    怎样在iPhone情侣模式中管理消息通知?自定义提示音的技巧怎样在iPhone情侣模式中管理消息通知?自定义提示音的技巧怎样在iPhone情侣模式中管理消息通知?自定义提示音的技巧怎样在iPhone情侣模式中管理消息通知?自定义提示音的技巧

    答案:通过自定义提示音、专注模式和沟通策略管理情侣间消息通知。首先为情侣设置专属短信铃声,利用GarageBand制作个性化音效;其次在微信中为特定联系人开启消息免打扰或独立提示音;再通过iOS“专注模式”创建“情侣模式”,仅允许其通知,并设定自动启用时段;若遇不重要通知,可长按关闭或暂时静音联系人…

    2026年9月22日 用户投稿
    000
  • Laravel 文件上传:解决数据库存储物理路径而非可访问 URL 的问题

    本教程旨在解决 laravel 文件上传后,数据库中存储文件物理路径而非可访问 url 的常见问题。通过分析 move() 方法的返回值,并引入 url() 辅助函数,我们将演示如何正确地将文件移动到指定目录,同时确保数据库记录的是可供前端访问的图片资源链接,从而避免图片无法正常显示。 在 Lara…

    2026年9月22日
    000
  • windows怎么更改计算机工作组_Windows计算机工作组修改方法

    首先通过系统属性修改工作组名称,右键“此电脑”选择属性,进入高级系统设置的计算机名选项卡进行更改并重启;其次可用管理员命令提示符执行wmic命令批量配置,输入指定命令后重启生效;最后专业版用户可通过组策略编辑器,在启动脚本中添加指令实现自动加入工作组。 如果您需要将Windows计算机加入或更改到特…

    2026年9月22日
    000
  • 机械键盘轴体深度手感分析:线性轴、段落轴与提前段落轴

    机械键盘手感取决于轴体类型,主流分为线性轴、段落轴和提前段落轴。线性轴直上直下顺滑连贯,代表如Cherry MX Red,适合游戏与快速输入;段落轴中程有明显阻力峰,提供清晰反馈,如Cherry MX Blue,适合文字工作;提前段落轴起步阻力大随后变轻,如TTC Gold Pink,防误触且节奏独…

    2026年9月22日
    000
  • 实现Java双向路径搜索的正确方法

    本文旨在帮助开发者理解并正确实现Java中的双向路径搜索算法。通过分析常见的实现错误,我们将提供一种清晰、可行的解决方案,并详细解释如何构建完整的路径,克服单向搜索树的局限性,从而实现从起点到终点的完整路径搜索。 双向路径搜索是一种优化路径搜索效率的策略,它同时从起点和终点开始搜索,并在中间相遇。然…

    2026年9月22日
    900
  • 如何在iPhone情侣模式中设置双人日历提醒?确保约会准时的技巧

    如何在iPhone情侣模式中设置双人日历提醒?确保约会准时的技巧如何在iPhone情侣模式中设置双人日历提醒?确保约会准时的技巧如何在iPhone情侣模式中设置双人日历提醒?确保约会准时的技巧如何在iPhone情侣模式中设置双人日历提醒?确保约会准时的技巧

    最核心的办法是使用iPhone的“共享日历”功能。首先创建共享日历并邀请伴侣加入,接着在日历中添加事件并设置双重提醒(如提前2小时和15-30分钟),确保双方开启日历通知权限,并检查iCloud同步状态以避免提醒延迟。通过添加地点、利用位置提醒、设置事件颜色分类、备注重要信息及结合“提醒事项”App…

    2026年9月22日 用户投稿
    100
  • VSCode设置Markdown写作环境(实用技巧,排版美化指南)

    要在vscode里打造舒服又高效的markdown写作环境,答案是通过安装核心扩展并进行个性化配置来实现;需安装markdown all in one、markdown preview enhanced、prettier和paste image等扩展,结合settings.json中的编辑器设置、自…

    2026年9月22日
    100
  • 如何用Filmora制作高质量AI视频?简易AI视频剪辑的实用指南

    如何用Filmora制作高质量AI视频?简易AI视频剪辑的实用指南如何用Filmora制作高质量AI视频?简易AI视频剪辑的实用指南如何用Filmora制作高质量AI视频?简易AI视频剪辑的实用指南如何用Filmora制作高质量AI视频?简易AI视频剪辑的实用指南

    Filmora的AI功能通过AI Copilot脚本生成、AI文本转视频、AI语音、图像生成、智能抠像及音频优化等工具,显著提升视频制作效率与专业度,尤其在视觉处理、听觉优化和创意辅助方面表现突出;关键在于将AI作为辅助起点,避免过度依赖,结合人工精修,才能实现高质量AI视频创作。 ☞☞☞AI 智能…

    2026年9月22日 用户投稿
    400
  • 好用的终端复用神器-Tmux

    好用的终端复用神器-Tmux好用的终端复用神器-Tmux好用的终端复用神器-Tmux好用的终端复用神器-Tmux

    前言 许久之前就听说过tmux,但是一直没上手,直到最近需要一直在linux下完成一些任务,我才切实感受到了tmux的优点:任意分屏、保存工作 就单单这两点,就足够实用了。分屏,曾今还十分痴迷i3wm和dwm这样的窗口管理工具,尤其是dwm的操作逻辑,大大提升linux工作效率。其他详情可以查看阮一…

    2026年9月22日 用户投稿
    100
  • LINUX如何比较两个文件的差异_Linux使用diff命令比较文件差异

    diff命令用于比较文件差异,基本用法为diff file1 file2,输出显示修改、添加或删除的行;结合-u、-i、-w等选项可提升可读性,常用于比较配置文件、代码版本、生成补丁(diff -u生成.patch文件)及验证文件一致性。 在Linux系统中,比较两个文件的差异是日常运维、开发和配置…

    2026年9月22日
    000
  • 微信怎么清理不常联系的好友 微信好友管理与批量清理技巧

    可通过查看聊天记录、使用标签分类、借助第三方工具及批量删除等方式清理微信中长期未互动的好友,优化好友列表。 如果您发现微信好友列表中存在大量长期未互动的联系人,导致聊天界面杂乱或查找不便,可以通过以下方法识别并清理不常联系的好友。这些操作有助于优化好友结构,提升沟通效率。 本文运行环境:iPhone…

    2026年9月22日
    100
  • 星纪魅族万志强回应魅族22影像升级:10月还会有OTA

    10月13日,星纪魅族集团中国区cmo万志强就用户对魅族22手机影像表现的积极评价作出回应。他表示,本月还将推送新一轮ota更新,届时魅族22的影像性能有望再次提升。 魅族22 据CNMO消息,有用户反馈称:尽管魅族22在发布时拍照能力并非顶尖,但通过数月的系统优化,其影像水准已达到主流旗舰机型80…

    2026年9月22日
    000
  • 伊瑟初始号开荒角色怎么刷

    伊瑟初始号开荒角色怎么刷伊瑟初始号开荒角色怎么刷伊瑟初始号开荒角色怎么刷伊瑟初始号开荒角色怎么刷

    伊瑟9月25日公测来袭,开荒选角是关键!选对初始角色,就如同给游戏之旅装上强力引擎,推图、拿资源效率飙升。但哪些是核心T0必刷角色,又有哪些要避坑?还有低成本养成策略大公开!这份完整开荒角色养成攻略,助你轻松起步,快来一探究竟! 伊瑟初始号开荒角色怎么刷 01开荒初始角色怎么刷 1.初始核心角色推荐…

    2026年9月22日 用户投稿
    200
  • 如何在DaVinciResolve中制作AI视频?教你利用AI工具优化视频流程

    如何在DaVinciResolve中制作AI视频?教你利用AI工具优化视频流程如何在DaVinciResolve中制作AI视频?教你利用AI工具优化视频流程如何在DaVinciResolve中制作AI视频?教你利用AI工具优化视频流程如何在DaVinciResolve中制作AI视频?教你利用AI工具优化视频流程

    达芬奇Resolve并非一键生成AI视频的%ignore_a_1%,而是通过内置AI功能与外部AI服务协同,提升视频制作效率。其核心在于利用Neural Engine驱动的智能工具,如Magic Mask实现精准抠像、Voice Isolation分离人声、Smart Reframe适配多平台构图、…

    2026年9月22日 用户投稿
    700
  • 2_准备开发环境

    2_准备开发环境2_准备开发环境2_准备开发环境2_准备开发环境

    第二章 准备开发环境 2.1 100ASK_IMX6ULL开发板的接线与启动 在接下来的操作中,我们将通过串口与开发板进行“交流”。串口,即串行接口,是指数据按顺序逐位传输,其特点是通信线路简单。安装好MobaXterm后,使用micro USB数据线连接电脑和开发板上的6号接口(USB转串口)。 …

    2026年9月22日 用户投稿
    100
  • 蔡司 2 亿影像王牌登场!vivo X300 Pro 拍巨片,巨出片!

    蔡司 2 亿影像王牌登场!vivo X300 Pro 拍巨片,巨出片!蔡司 2 亿影像王牌登场!vivo X300 Pro 拍巨片,巨出片!蔡司 2 亿影像王牌登场!vivo X300 Pro 拍巨片,巨出片!蔡司 2 亿影像王牌登场!vivo X300 Pro 拍巨片,巨出片!

    在手机影像技术竞争愈发白热化的当下,vivo x300 pro 以“蔡司 2 亿影像王牌”之名强势亮相。其核心亮点莫过于搭载的蔡司 2 亿像素影像系统,相较传统多摄组合实现了显著跃升。面对用户日益多元的需求——远摄、微距、视频创作样样都想兼顾,这套系统真正做到了“全都要”。起售价为 5299 元,这…

    2026年9月22日 用户投稿
    000
  • Java项目类路径管理:引用与实现外部.class文件定义的接口

    在Java项目中引用并实现由.class文件定义的接口,核心在于正确配置Java的类路径(Classpath)。本文将详细介绍类路径的概念、其重要性,以及如何在命令行和集成开发环境(IDE)中有效地设置类路径,确保编译器和JVM能够找到所需的.class文件,从而成功编译和运行包含外部接口实现的代码…

    2026年9月22日
    000

发表回复

登录后才能评论
关注微信