使用Java Stream高效实现分组并获取最大值映射

使用Java Stream高效实现分组并获取最大值映射

本文旨在探讨如何利用java stream api,特别是`collectors.tomap`结合合并函数,高效地将对象列表按指定属性分组,并为每个分组选出特定属性值最大的对象,最终生成一个映射(map)。我们将对比传统`groupingby`的局限性,并展示如何通过优化后的stream操作实现更简洁、高效的代码。

1. 场景概述与问题背景

在数据处理中,我们经常遇到这样的需求:给定一个包含多个对象的列表,需要根据某个属性对这些对象进行分组,并且在每个分组中,只保留满足特定条件(例如,某个数值属性最大)的一个对象。最终,我们希望将结果组织成一个Map,其中键是分组依据的属性值,值是选出的代表对象。

例如,我们有一个StudentGrade类,包含studentId、value(成绩)和date等信息:

import java.util.Date;public class StudentGrade {    int studentId;    double value;    Date date;    // 构造函数    public StudentGrade(int studentId, double value, Date date) {        this.studentId = studentId;        this.value = value;        this.date = date;    }    // Getter 方法    public int getStudentId() {        return studentId;    }    public double getValue() {        return value;    }    public Date getDate() {        return date;    }    @Override    public String toString() {        return "StudentGrade{" +               "studentId=" + studentId +               ", value=" + value +               ", date=" + date +               '}';    }}

我们的目标是获取每个学生的最高成绩记录,并以Map的形式返回,其中键是studentId,值是该学生对应的最高成绩StudentGrade对象。

2. 初始实现及局限性分析

一种常见的初步思路是使用Collectors.groupingBy结合Collectors.maxBy。这种方法首先将学生成绩按studentId分组,然后在每个分组中找到value最大的StudentGrade。然而,Collectors.maxBy返回的是Optional,这意味着我们需要额外的步骤来处理Optional并将其放入最终的Map中。

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

import java.util.*;import java.util.stream.Collectors;public class GradeProcessorInitial {    public Map getMaxGradeByStudent(List grades) {        Map<Integer, Optional> maxGradesOptional = grades.stream().collect(            Collectors.groupingBy(                StudentGrade::getStudentId,                Collectors.maxBy(Comparator.comparing(StudentGrade::getValue)))        );        Map finalGrades = new HashMap();        maxGradesOptional.entrySet().forEach(entry -> {            entry.getValue().ifPresent(value -> finalGrades.put(entry.getKey(), value));        });        return finalGrades;    }}

上述方法虽然能够实现功能,但存在以下几点不足:

Elser AI Comics Elser AI Comics

一个免费且强大的AI漫画生成工具,助力你三步创作自己的一出好戏

Elser AI Comics 522 查看详情 Elser AI Comics 中间Optional层级: Collectors.maxBy返回Optional,需要后续处理来解包。额外的Map初始化和迭代: 需要创建一个新的HashMap,并对maxGradesOptional的entrySet进行迭代,将解包后的值放入新Map中,这增加了代码的复杂性和运行时的开销。不够Stream化: 整个过程并非完全在一个Stream管道中完成,有明显的“分段”操作。

3. 优化方案:使用 Collectors.toMap 与合并函数

Java Stream API提供了更简洁、高效的方式来解决这类问题,即使用Collectors.toMap的三参数版本,它允许我们指定一个合并函数(mergeFunction)来处理键冲突。当多个元素映射到同一个键时,合并函数会决定保留哪一个元素。

3.1 Collectors.toMap 的三参数版本

Collectors.toMap的签名如下:public static Collector<T, ?, Map> toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)

keyMapper:用于从输入元素中提取键的函数。valueMapper:用于从输入元素中提取值的函数。mergeFunction:一个BinaryOperator,当多个元素映射到同一个键时,该函数用于合并这些元素的值。

3.2 优化后的实现

针对我们的StudentGrade问题,我们可以这样构建Collectors.toMap:

keyMapper: 显然是StudentGrade::getStudentId,将studentId作为Map的键。valueMapper: 我们希望将整个StudentGrade对象作为Map的值,所以可以使用Function.identity()或x -> x。mergeFunction: 这是关键。当两个StudentGrade对象具有相同的studentId时,我们需要比较它们的value属性,并保留value更大的那个。这可以通过BinaryOperator.maxBy(Comparator.comparing(StudentGrade::getValue))来实现。

import java.util.*;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.stream.Collectors;public class GradeProcessorOptimized {    public Map getMaxGradeByStudent(List grades) {        return grades.stream()            .collect(Collectors.toMap(                StudentGrade::getStudentId,       // keyMapper: 以 studentId 为键                Function.identity(),              // valueMapper: 以 StudentGrade 对象本身为值                BinaryOperator.maxBy(Comparator.comparing(StudentGrade::getValue)) // mergeFunction: 遇到相同键时,保留 value 最大的对象            ));    }    public static void main(String[] args) {        List studentGrades = Arrays.asList(            new StudentGrade(1, 85.0, new Date()),            new StudentGrade(2, 90.0, new Date()),            new StudentGrade(1, 92.0, new Date()), // 学生1的更高成绩            new StudentGrade(3, 78.0, new Date()),            new StudentGrade(2, 88.0, new Date()), // 学生2的较低成绩            new StudentGrade(1, 70.0, new Date())        );        GradeProcessorOptimized processor = new GradeProcessorOptimized();        Map maxGrades = processor.getMaxGradeByStudent(studentGrades);        System.out.println("每个学生的最高成绩记录:");        maxGrades.forEach((studentId, grade) ->            System.out.println("学生ID: " + studentId + ", 最高成绩: " + grade.getValue())        );        // 验证学生1的最高成绩        System.out.println("学生1的完整最高成绩记录: " + maxGrades.get(1));    }}

运行结果示例:

每个学生的最高成绩记录:学生ID: 1, 最高成绩: 92.0学生ID: 2, 最高成绩: 90.0学生ID: 3, 最高成绩: 78.0学生1的完整最高成绩记录: StudentGrade{studentId=1, value=92.0, date=Mon Jan 01 00:00:00 CST 2024} // 日期会是当前运行时的日期

3.3 优势分析

简洁性: 代码更加紧凑和富有表达力,所有逻辑都在一个Stream管道中完成。效率: 避免了中间Optional对象的创建和额外的Map迭代,通常在一次遍历中即可完成所有操作。纯Stream操作: 完美地融入了Java Stream的声明式编程风格。直接输出所需类型: 直接返回Map,无需后续转换。

4. 注意事项与最佳实践

mergeFunction的重要性: Collectors.toMap的第三个参数——合并函数,是处理键冲突的关键。如果省略此参数(使用两参数版本),当存在重复键时,Stream会抛出IllegalStateException。因此,理解并正确使用合并函数至关重要。BinaryOperator与Comparator: BinaryOperator是一个函数式接口,接受两个相同类型的参数并返回一个相同类型的结果。BinaryOperator.maxBy()和BinaryOperator.minBy()是其工厂方法,它们接受一个Comparator来定义比较规则。选择合适的Collector: 在处理分组和聚合问题时,Collectors.groupingBy和Collectors.toMap各有其适用场景。groupingBy通常用于需要将所有相同键的元素收集到一个列表中(例如Map<K, List>),或者进行更复杂的下游收集器操作(例如Map计数,Map求平均值)。toMap更适用于当每个键最终只对应一个值,并且需要处理键冲突(如选择最大/最小/最新等)的场景。可读性: 尽管代码更简洁,但对于不熟悉Stream API的开发者来说,三参数toMap可能需要一些时间来理解。适当的注释可以提高代码的可读性。

5. 总结

通过本文的讲解,我们了解了如何利用Java Stream API中的Collectors.toMap结合合并函数,优雅且高效地解决按属性分组并获取最大值的问题。相较于使用Collectors.groupingBy再进行额外处理的方案,优化后的方法不仅代码更简洁、可读性更强,而且在性能上也通常更优。掌握这种模式,能够帮助开发者编写出更符合现代Java编程范式的Stream处理代码。

以上就是使用Java Stream高效实现分组并获取最大值映射的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
京东京造JDBook笔记本电脑怎么一键重装Win7系统操作教学
上一篇 2025年11月28日 16:48:43
《五菱汽车》扫码充电方法
下一篇 2025年11月28日 16:48:51

相关推荐

  • mysql索引类型有哪些 mysql创建不同索引的方法对比

    mysql索引类型有哪些 mysql创建不同索引的方法对比mysql索引类型有哪些 mysql创建不同索引的方法对比mysql索引类型有哪些 mysql创建不同索引的方法对比mysql索引类型有哪些 mysql创建不同索引的方法对比

    mysql支持多种索引类型,选择合适的索引类型可提升数据库性能。1.b-tree索引适用于等值、范围查询和排序,是innodb和myisam的默认索引;2.hash索引仅适合等值查询,不支持范围和排序,memory引擎支持显式创建;3.fulltext索引用于文本搜索,适合关键词查找;4.空间索引(…

    2026年9月23日 用户投稿
    000
  • 京东自营外卖门店“七鲜小厨”入驻美团

    10 月 13 日消息,据电商派今日报道,京东自营外卖门店“七鲜小厨”已正式登陆美团 app。与此同时,京东全新推出的独立咖啡品牌“七鲜咖啡”也同步上线美团平台。 京东首家“七鲜小厨”自营外卖门店于今年7月20日在北京市东城区开业,采用“外卖 + 自提”的运营模式,不设堂食服务,用户可通过线上渠道下…

    2026年9月23日
    000
  • QQ音乐会员退订后还能听吗_QQ音乐会员退订后听歌的说明

    退订QQ音乐会员后将无法享受高音质、无广告等权益,系统自动切换至免费模式。此时仅可播放标有“免费”或无版权标识的歌曲,VIP歌曲需开通会员才能畅听。已下载的加密格式会员歌曲(如.QMC、.TMF)在会员过期后无法继续播放,需重新开通会员解密。免费用户可通过观看广告解锁每日最多5首歌曲完整播放,每次看…

    2026年9月23日
    300
  • Tableau的AI混合工具如何操作?生成智能数据可视化的实用指南

    Tableau的AI混合工具通过自然语言查询、自动解释和预测模型,降低数据分析门槛,帮助非技术用户快速获取洞察。首先,Ask Data支持用日常语言提问,自动生成可视化图表,显著提升数据探索效率;其次,Explain Data利用机器学习分析异常点,揭示潜在影响因素,将“是什么”转化为“为什么”;再…

    2026年9月23日
    000
  • mysql安装完成如何事件 mysql定时任务设置教程

    mysql安装完成如何事件 mysql定时任务设置教程mysql安装完成如何事件 mysql定时任务设置教程mysql安装完成如何事件 mysql定时任务设置教程mysql安装完成如何事件 mysql定时任务设置教程

    要使用mysql的事件调度器设置定时任务,首先需开启事件调度器,其次创建定时事件,再查看管理事件,最后注意权限与时间格式等问题。具体步骤如下:1. 开启事件调度器:通过命令或配置文件启用;2. 创建事件:使用create event定义执行频率与sql操作;3. 管理事件:可查看、修改或删除已有事件…

    2026年9月23日 用户投稿
    100
  • OpenAI 与微软达成重磅交易:股权结构再变,投资者面临稀释风险

    据《金融时报》披露,OpenAI 近期完成了一系列关键性交易,使其股权架构日趋复杂,同时也加剧了投资者对未来收益前景的担忧。在这些新协议推动下,OpenAI 的估值已飙升至5000亿美元,跃居全球最具价值的未上市企业之列。这一惊人估值的背后,是公司与英伟达和AMD两家芯片巨头达成的数十亿美元合作协议…

    2026年9月23日
    000
  • 抖音短视频被系统判定违规怎么办 抖音内容管理与违规申诉方法

    先明确违规原因,再通过APP申诉并提交原创或授权证据,必要时邮件、电话多渠道沟通,确保材料真实完整。 抖音视频被系统判定违规,先别急着申诉,关键是要搞清楚为什么会被判。平台的审核机制有时会出现误判,但也可能是内容确实踩了红线。处理的核心是精准定位问题、准备充分证据、通过正确渠道沟通。下面分几步说明怎…

    2026年9月23日
    300
  • NS2版《无主之地4》突遭延期!预购将取消

    《无主之地4》现可提前购入,使用金币叠加限时优惠券后,标准版仅需244.5元(共节省 ¥53.5);超级豪华版为457.4元(总计优惠 ¥100.6)。 原计划于10月3日发布的《无主之地4》Nintendo Switch 2版本已确认延期。Gearbox Entertainment最新发布公告称,…

    2026年9月23日
    200
  • 如何在mysql中优化多表JOIN查询

    答案:优化MySQL多表JOIN需创建关联字段索引、提前过滤数据、选择合适JOIN类型与表序、利用EXPLAIN分析执行计划,并定期更新统计信息以提升查询效率。 在MySQL中优化多表JOIN查询,关键在于减少数据扫描量、提升连接效率,并合理利用索引和执行计划。以下是一些实用的优化策略。 1. 确保…

    2026年9月23日
    300
  • WooCommerce 购物车联动:实现赠品自动添加与移除的专业指南

    本文提供了一份关于在 woocommerce 中实现自动赠品系统的全面指南。它解决了在程序化添加产品时常见的 `woocommerce_add_to_cart` 递归问题,并提供了一个使用自定义购物车项元数据来管理关联赠品的健壮解决方案,确保赠品能与特定主产品同步添加和移除。 引言 在电子商务中,为…

    2026年9月23日
    500
  • 苹果手机USB调试模式开启方法

    准备工作 在操作前,请确保你的iPhone已连接网络,并升级至最新的iOS系统版本。同时,准备一台安装了最新版iTunes(Windows)或Finder(macOS)的电脑,以确保设备能够被正确识别和管理。 步骤一:开启相关调试功能 打开iPhone上的“设置”应用。 进入“Safari”浏览器设…

    2026年9月23日
    100
  • Java Web项目在无Maven/Eclipse环境下生成WAR包的实践指南

    本文详细介绍了如何在没有Maven或Eclipse等集成开发环境或构建工具的情况下,为Java Web项目手动或通过Apache Ant工具生成WAR文件。教程涵盖了WAR文件的基本结构、使用Ant进行编译和打包的具体步骤,并提供了Ant构建脚本示例,旨在帮助开发者理解并实践WAR包的独立构建过程。…

    2026年9月23日
    100
  • MySQL安装需要哪些硬件配置要求?

    MySQL安装需要哪些硬件配置要求?MySQL安装需要哪些硬件配置要求?MySQL安装需要哪些硬件配置要求?MySQL安装需要哪些硬件配置要求?

    mysql的硬件配置需根据应用场景和负载决定,生产环境应重点考虑磁盘i/o、内存、cpu和网络。1. cpu:oltp场景多核心更重要,olap则更依赖主频和缓存;2. 内存:buffer pool越大越好,但需避免过度分配导致swap使用;3. 磁盘i/o:ssd是标配,nvme ssd和raid…

    2026年9月23日 用户投稿
    200
  • 硬核推理游戏《机密谋杀案中案》参加Steam新品节 试玩版上线

    硬核推理游戏《机密谋杀案中案》参加Steam新品节 试玩版上线硬核推理游戏《机密谋杀案中案》参加Steam新品节 试玩版上线硬核推理游戏《机密谋杀案中案》参加Steam新品节 试玩版上线硬核推理游戏《机密谋杀案中案》参加Steam新品节 试玩版上线

    如果你已经顺利解开《奥伯拉丁的回归》或《金偶像迷案》中的重重谜团,那么接下来的挑战将更加扑朔迷离!好莱坞正陷入一场震惊全城的连环谋杀风暴!你将化身为一名敏锐过人的侦探,运用你的观察力与推理能力:勘察犯罪现场,搜集关键证据,抽丝剥茧地还原真相。幕后黑手究竟是谁?他又为何精心策划这一系列隐秘的杀局? 这…

    2026年9月23日 用户投稿
    100
  • 如何在Procreate中使用AI导出图片?保存高质量图像的正确方法

    Procreate无内置AI导出功能,但可通过导出高质量图像(如PSD、TIFF、PNG)供外部AI工具优化;选择格式需根据用途,PSD适合协作,TIFF用于印刷,PNG支持透明背景,JPEG慎用以避免压缩损失;画布应高DPI创建,色彩配置优先sRGB,印刷时后期转CMYK更精准。 ☞☞☞AI 智能…

    2026年9月23日
    100
  • safari浏览器与iCloud钥匙串同步失败如何解决_safari浏览器钥匙串同步失败解决方法

    首先检查iCloud钥匙串是否在所有设备上开启且使用同一Apple ID,确认已启用双重认证;接着重启设备并重新开启钥匙串功能以修复临时故障;然后确保网络稳定并查看Apple服务器状态正常;最后通过退出并重新登录Apple ID重建同步授权,恢复密码自动填充与跨设备同步。 如果您在使用Safari浏…

    2026年9月23日
    200
  • 优麒麟 25.10 版本正式发布

    优麒麟 25.10 正式版现已上线,此版本将提供长达9个月的支持周期,基于最新的 linux 6.17 内核打造,在基础库、子系统及核心组件等方面实现了全面升级,显著提升了系统的稳定性与兼容性,同时推出了焕然一新的软件商店。 新增特性 1. 搭载 Linux 6.17 内核 优麒麟 25.10 集成…

    2026年9月23日
    100
  • linux如何优雅的关机

    优雅关机的三大法宝:拔电源、shutdown、poweroff 及其对硬件和数据的影响 在讨论关机方法之前,先了解一下机械硬盘的内部结构。 那固态硬盘SSD呢? FTL工作示意图。FTL表对SSD至关重要,如果在FTL写回Flash之前突然断电,内存数据丢失,FTL表也将丢失。因此,高端SSD和服务…

    2026年9月23日
    100
  • PHP自定义函数:创建与使用 prev_id() 函数的实践指南

    本文旨在指导读者如何定义和实现自定义PHP函数,以解决“Call to undefined function”错误。通过 prev_id() 函数的创建示例,详细阐述了函数的基本语法、参数传递、返回值以及在实际应用(如数据库查询)中的集成方法,并提供了关键注意事项,帮助开发者编写模块化、可维护的代码…

    2026年9月23日
    100
  • 四种获取fasta序列长度的方法

    在处理fasta序列时,我们常常需要知道每条序列的长度。今天小编将与大家分享四种获取fasta序列长度的方法。 一、使用awk 以下是使用awk获取fasta序列长度的代码: awk ‘/^>/{if (l!=””) print l; print; l=0; next}{l+=length($…

    2026年9月23日
    200

发表回复

登录后才能评论
关注微信