Collectors.toMap报错:空指针 key重复

news2025/2/27 5:30:34

Java 8中的stream在项目开发中被同学们用的风生水起,当然大家也踩了不少坑。下面我就来说说Collections.toMap在项目使用中踩的坑,避免大家重复被坑。


一.介绍Collectors.toMap

Collectors.toMap 是 Java 8 中的一个收集器,它可以将流中的元素转换为 Map 对象,其中每个元素的 key 由指定的函数生成。

当我们使用 Collectors.toMap 方法时,可能会遇到重复的 key 问题,这是因为我们在将元素转化为 Map 对象时,如果两个元素具有相同的 key,则会发生冲突,抛出异常。

还可能会遇到value为null的问题,这是因为我们在将元素转化为 Map 对象时,toMap最终是调用了Map.merge方法,merge方法不允许value为null 导致的异常抛出。

二.问题复现与分析以及解决方案

1、Collectors.toMap的key重复问题

问题复现:
    public static void main(String[] args) {
        List<BenefitModel> benefitModelList = new ArrayList<>();
        benefitModelList.add(new BenefitModel("123", "积分权益"));
        benefitModelList.add(new BenefitModel("123", "现金权益"));
        Map<String, String> benefitMap = benefitModelList.stream().collect(Collectors.toMap(BenefitModel::getBenefitId, BenefitModel::getBenefitName));
        System.out.println(JSON.toJSONString(benefitMap));
    }
运行结果:

原因分析:

查看Collectors.toMap源码如下,

toMap最终是调用了Map.merge方法,传入的mergeFunction是throwingMerger直接抛出异常,日志信息使用的是第一个参数u。传入的mapSupplier是HashMap对象(HashMap::new)。所以最终会调用到HashMap.merge。

而在HashMap.merge中,对于mergeFunction的应用如下:

在HashMap.merge的语义中,mergeFunction用于合并value,比如对于key的计数,可以使用map.merge(key, 1, Integer::sum)。若不存在则置1,存在则+1。这里的入参是oldValue和newValue。

所以最终传递给throwingMerger的两个参数就不是k-v了。所以报错的所谓Duplicate key其实是oldValue。

解决方案:
  • 保证toMap的key不重复
  • 调用重载方法,主动指定当key重复时,需要做的合并操作(合并规则可以根据业务需要,自定义)

于是上面重复key的代码优化后为:(合并规则:重复key出现时,取后面的,前面的丢弃)

    public static void main(String[] args) {
        List<BenefitModel> benefitModelList = new ArrayList<>();
        benefitModelList.add(new BenefitModel("123", "积分权益"));
        benefitModelList.add(new BenefitModel("123", "现金权益"));
        Map<String, String> map = benefitModelList.stream()
                .collect(Collectors.toMap(BenefitModel::getBenefitId, BenefitModel::getBenefitName,
                                          (k1, k2) -> k2));
        System.out.println(JSON.toJSONString(map));
    }
高版本JDK的修复措施:

重复key这个问题在后续版本中得到修复,比如在JDK 11中的处理。

2、Collectors.toMap的value值为null问题

问题复现:
    public static void main(String[] args) {
        List<BenefitModel> benefitModelList = new ArrayList<>();
        benefitModelList.add(new BenefitModel("123", "积分权益"));
        benefitModelList.add(new BenefitModel("124", null));
        Map<String, String> benefitMap = benefitModelList.stream().
        collect(Collectors.toMap(BenefitModel::getBenefitId, BenefitModel::getBenefitName));
        System.out.println(JSON.toJSONString(benefitMap));
    }
运行结果:

原因分析:

有问题,看源码,查看Collectors.toMap源码如下,

toMap最终是调用了Map.merge方法,而在HashMap.merge中,对于value的应用如下:

在HashMap.merge的语义中,value使用前需要进行判空处理,null直接抛出异常NullPointerException。

解决方案:

方案1:先把value为null的数据过滤掉,再用Collectors.toMap。

        Map<String, String> map2 = benefitModelList.stream()
                .filter(m -> m.getBenefitName() != null)
                .collect(Collectors.toMap(BenefitModel::getBenefitId, BenefitModel::getBenefitName));

方案2:查资料评价度最好的方案如下。其实跟你方案1中思路-手动foreach一毛一样。

        Map<String, String> map2 = benefitModelList.stream().collect(HashMap::new, 
                (m, v) -> m.put(v.getBenefitId(), v.getBenefitName()),
                HashMap::putAll);
高版本JDK的修复措施:

Collectors.toMap使用时,value值为null,这个问题在Java 11中仍然存在。可能value为null,这种数据很少见,促使解决过程比较缓慢。

三、Collectors.toMap使用总结

综上所以,在使用Collectors.toMap时需要记住几点:

1、key不能有重复,否则会报错IllegalStateException: Duplicate key,因为Map的key不能重复。

2、value不能为空,否则报错NullPointerException。

看完了本文,你可以去搜搜你的项目代码中使用Collectors.toMap的地方,有没有可能踩上面的坑。不要说你的业务数据不会出现重复key的数据,不会出现value值null的情况,上百万的业务数据,什么情况都会有的。

参考资料:java - Ignore duplicates when producing map using streams - Stack Overflow

java - NullPointerException in Collectors.toMap with null entry values - Stack Overflow

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1158510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ArrayList简介及使用全方位手把手教学(带源码),用ArrayList实现洗牌算法,3个人轮流拿牌(带全部源码)

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信你对这篇博客也感兴趣o (ˉ▽ˉ&#xff1b;) &#x1f4dc;数据结构 —— Java自定义代码实现顺序表&#xff0c;包含测试用例以及ArrayList的使用以及相关算法题(带完整源码) 目录 ArrayList简介 Arra…

Spring Security 6.1.x 系列(2)—— 基于过滤器的基础原理(一)

一、过滤器 Spring Security 的 Servlet 支持基于 Servlet 过滤器&#xff0c;因此首先了解过滤器的作用会很有帮助。 下图为单个 HTTP 请求的处理程序的典型分层。 客户端向应用程序发送一个请求&#xff0c;运行容器创建一个FilterChain&#xff08;过滤链&#xff09;&…

Thinkphp6项目在虚拟机无法指向pulic的目录访问的方法

以阿里云虚拟主机为例&#xff0c;服务器环境为 LAMP&#xff0c;Apache2.4 php7.2 mysql5.7 1.根目录新建 index.php 文件&#xff0c;将以下内容放入文件中 <?php include ./public/index.php;2.将 public 目录下的 admin.php、backend 文件夹、static 文件夹、tinymc…

UWB 技术在机器人和移动领域的应用题】

多年来&#xff0c;机器人生态系统不断增长&#xff0c;不同的应用程序也在不断增长。如今&#xff0c;机器人出现在许多不同的领域&#xff0c;例如私人家庭、商业场所、仓库和医疗场所。他们要么自主工作&#xff0c;要么与我们并肩工作&#xff0c;帮助我们完成任务。 根据…

Web APIs——日期对象的使用

1、日期对象 日期对象&#xff1a;用来表示时间的对象 作用&#xff1a;可以得到当前系统时间 1.1实例化 在代码中发现了new关键字时&#xff0c;一般将这个操作称为实例化 创建一个时间对象并获取时间 获得当前时间 const date new Date() <script>// 实例化 new //…

R语言的DICE模型实践技术

随着温室气体排放量的增大和温室效应的增强&#xff0c;全球气候变化问题受到日益的关注。我国政府庄严承诺在2030和2060年分别达到“碳达峰”和“碳中和”&#xff0c;因此气候变化和碳排放已经成为科研人员重点关心的问题之一。气候变化问题不仅仅是科学的问题&#xff0c;同…

php得到两个数组之间的差集、并集、交集方法

1、差集&#xff1a; array_diff()函数用于返回在第一个数组中存在&#xff0c;但在其他数组中不存在的值。 $array1 [1, 2, 3, 4, 5]; $array2 [4, 5, 6, 7, 8]; $diff array_diff($array1, $array2); print_r($diff); 输出&#xff1a;Array ( [0] > 1 [1] > 2 [2]…

网络安全(黑客)-小白自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

学习笔记|两独立样本秩和检验|曼-惠特尼 U数据分布图|规范表达|《小白爱上SPSS》课程:SPSS第十二讲 | 两独立样本秩和检验如何做?

目录 学习目的软件版本原始文档两独立样本秩和检验一、实战案例二、统计策略三、SPSS操作1、正态性检验2、两样本秩和检验 四、结果解读疑问&#xff1a;曼-惠特尼 U数据分布图如何绘制&#xff1f; 五、规范报告1、规范表格2、规范文字 六、划重点 学习目的 SPSS第十二讲 | 两…

Linux根目录下的目录结构及其作用详解

Linux根目录是文件系统的最顶层&#xff0c;它包含了一些子目录&#xff0c;每个子目录都有特定的功能和存储的文件。只有了解了各个文件的使用功能&#xff0c;才能更好的去使用Linux系统。希望通过下面这张图能够让你更加了解根目录下的各个目录的功能。

Redis通过复制rdb文件方式同步线上数据到本地以及提示:Can‘t handle RDB format version 9解决

场景 Redis的持久化机制-RDB方式和AOF方式&#xff1a; Redis的持久化机制-RDB方式和AOF方式_rdb 和ao-CSDN博客 Redis持久化机制导致服务自启动后恢复数据过长无法使用以及如何关闭&#xff1a; Redis持久化机制导致服务自启动后恢复数据过长无法使用以及如何关闭_霸道流氓…

「直播回放」使用 PLC + OPC + TDengine,快速搭建烟草生产监测系统

在烟草工业场景里&#xff0c;多数设备的自动控制都是通过 PLC 可编程逻辑控制器来实现的&#xff0c;PLC 再将采集的数据汇聚至 OPC 服务器。传统的 PI System、实时数据库、组态软件等与 OPC 相连&#xff0c;提供分析、可视化、报警等功能&#xff0c;这类系统存在一些问题&…

OpenCV标定演示,及如何生成标定板图片

标定的程序在官方的源码里有&#xff0c; opencv-4.5.5\samples\cpp\tutorial_code\calib3d\camera_calibration 很多小白不知道怎么跑起来&#xff0c;这个也怪OpenCV官方&#xff0c;工作没做完善&#xff0c;其实的default.xml是要自己手动改的&#xff0c;输入的图片也要…

MySQL -- 内置函数

MySQL – 内置函数 文章目录 MySQL -- 内置函数一、日期函数1.current_date()获取年月日2.current_time()获取时分秒3.current_timestamp() / now()获得时间戳4.date_add()在日期的基础上加日期5.date_sub()在日期的基础上减去日期6. datediff()计算两个日期之间相差多少天7.案…

给两个字符串,在第一字符串中删除第二个字符串中所包含的所有字符(Java版)

题目描述&#xff1a; 给定两个字符串&#xff1a;s1和s2 s1:welcome to world s2:come 要求在输出的结果中将s1中存在的s2的字符删除。 最终输出的结果&#xff1a;wl t wrld 这里将会用到数组来解决此问题。 首先&#xff0c;定义一个数组ArrayList(),其次将两个对比的字符串…

微服务框架SpringcloudAlibaba+Nacos集成RabbitMQ

目前公司使用jeepluscloud版本&#xff0c;这个版本没有集成消息队列&#xff0c;这里记录一下&#xff0c;集成的过程&#xff1b;这个框架跟ruoyi的那个微服务版本结构一模一样&#xff0c;所以也可以快速上手。 1.项目结构图&#xff1a; 配置类的东西做成一个公共的模块 …

unity 点击3D物体

1. 在场景中添加事件系统 2. 为主相机添加射线检测 3. 为物体挂载以下脚本&#xff0c;物体必须带碰撞体 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems;// 挂在物体上&#xff0c;需要添加碰撞体 public …

遥遥领先一个量级,平头哥发布首颗SSD主控芯片镇岳510

11月1日&#xff0c;在2023云栖大会上&#xff0c;阿里巴巴平头哥发布旗下首颗SSD主控芯片镇岳510&#xff0c;该芯片为云计算场景深度定制&#xff0c;实现4μs超低时延&#xff0c;比业界主流降低30%以上&#xff0c;误码率低至10^-18&#xff0c;比业内标杆领先一个数量级。…

目标检测(Object Detection): 你需要知道的一些概念

文章目录 NMS 非极大值抑制目的步骤 mAP&#xff08;Mean Average Precision&#xff09;步骤 Feature Pyramid Network 特征金字塔结构一阶段检测器Single-Stage Detectors"Anchor-based"的代表RetinaNetAnchor-free 的代表FCOS NMS 非极大值抑制 目的 去除网络输…

JS 去除字符串中所有标点符号

直接上代码了 var str 这是《书》中的一段&#xff0c;两段文字。; var new_str str.replace(/[:_.~!#$%^&*() \ <>?"{}|, \/ ; \\ [ \] ~&#xff01;#&#xffe5;%……&*&#xff08;&#xff09;—— \ {}|《》&#xff1f;&#xff1a;“”【】、&a…