Java8 Collectors.toMap抛出重复键50异常,键类型不符存疑求助
Collectors.toMap抛出Duplicate key 50但我的Map键是String类型? 我太懂这种困惑了!这个异常信息绝对是Java里最误导人的设计之一——你看到的50根本不是重复的键,而是重复键对应的值!
问题根源
当你使用Collectors.toMap()但没有指定合并函数时,如果流中有两个元素生成了相同的键(也就是两个Person对象的getNameInString()返回了完全一样的字符串),默认的合并逻辑会直接抛出IllegalStateException。但反直觉的是,异常信息里的“Duplicate key X”中的X,其实是第二个冲突元素的值,而不是重复的键本身。
放到你的场景里:你的listOfPeople中肯定存在两个Person对象,它们的名字(getNameInString()的返回值)完全相同,其中一个的年龄是50,另一个的年龄是其他数值。当toMap尝试把第二个年龄值放到同一个名字键下时,就触发了异常,但错误信息却把年龄值当成了键来显示,这直接把你引到了错误的排查方向上。
解决方案
根据你的业务需求,有两种常见的处理方式:
指定合并函数处理重复键
如果允许同一个名字对应多个年龄(或者你想保留其中一个),可以在toMap中添加第三个参数,定义遇到重复键时的合并规则:// 保留旧值(即先出现的那个人的年龄) Map<String, Integer> myMap = listOfPeople.stream() .collect(Collectors.toMap( Person::getNameInString, Person::getAgeInInt, (oldAge, newAge) -> oldAge )); // 或者保留新值(即后出现的那个人的年龄) Map<String, Integer> myMap = listOfPeople.stream() .collect(Collectors.toMap( Person::getNameInString, Person::getAgeInInt, (oldAge, newAge) -> newAge ));从数据层面解决重复问题
如果你的业务逻辑里不应该出现同名的Person,那就要去检查listOfPeople的数据源,找出那些名字重复的对象,从根源上消除数据重复。
下次再遇到这个异常,别再盯着“Duplicate key”后面的数字找键了——先去排查流中是否有元素生成了相同的键,那个数字只是冲突的值而已,这真的是Java这个API的小陷阱!
内容的提问来源于stack exchange,提问作者ohad edelstain

