Collectors.toMap 使用技巧 (List 转 Map超方便)
一、前言
1. 过去的做法(List 转 Map)
1 2 3 4 5 6 7 8 9 | List<User> userList = new ArrayList<>(); userList.add( new User().setId( "A" ).setName( "张三" )); userList.add( new User().setId( "B" ).setName( "李四" )); userList.add( new User().setId( "C" ).setName( "王五" )); //遍历 Map<String, String> map = new HashMap<>(); for (User user : userList) { map.put(user.getId(), user.getName()); } |
2. 如今我们可以利用Java8 中新增的 Stream 特性,使得操作集合更加的方便
使用Collectors.toMap
来代替之前的手写遍历,但是在阅读Java开发手册(嵩山版)后,发现了一个容易被忽视的隐患!
原文如下:
【强制】
在使用java.util.stream.Collectors
类的 toMap()
方法转为 Map 集合时,一定要使
用含有参数类型为BinaryOperator
,参数名为mergeFunction
的方法,否则当出现相同key
值时会抛出 IllegalStateException
异常。
说明:参数 mergeFunction
的作用是当出现 key
重复时,自定义对value
的处理策略。
正例:
1 2 3 4 5 6 7 | List<Pair<String, Double>> pairArrayList = new ArrayList<>( 3 ); pairArrayList.add( new Pair<>( "version" , 12.10 )); pairArrayList.add( new Pair<>( "version" , 12.19 )); pairArrayList.add( new Pair<>( "version" , 6.28 )); Map<String, Double> map = pairArrayList.stream().collect( // 生成的 map 集合中只有一个键值对:{version=6.28} Collectors.toMap(Pair::getKey, Pair::getValue, (v1, v2) -> v2)); |
反例:
1 2 3 4 | String[] departments = new String[] { "iERP" , "iERP" , "EIBU" }; // 抛出 IllegalStateException 异常 Map<Integer, String> map = Arrays.stream(departments) .collect(Collectors.toMap(String::hashCode, str -> str)); |
二、Collectors.toMap的详细使用技巧
开篇遍历的做法, 我们可以一行代码搞定:
1 | userList.stream().collect(Collectors.toMap(User::getId, User::getName)); |
当然,如果希望得到 Map
的value
为对象本身时,可以这样写:
1 | userList.stream().collect(Collectors.toMap(User::getId, t -> t)); |
或者:
1 | userList.stream().collect(Collectors.toMap(User::getId, Function.identity())); |
关于 Collectors.toMap 方法
Collectors.toMap
有三个重载方法:
1 2 3 4 5 | toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper); toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction); toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier); |
参数解释:
keyMapper
:Key
的映射函数valueMapper
:Value
的映射函数mergeFunction
:当Key
冲突时,调用的合并方法mapSupplier
:Map
构造器,在需要返回特定的Map
时使用
还是用上面的例子,如果 List 中 userId 有相同的,使用上面的写法会抛异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | List<User> userList = new ArrayList<>(); userList.add( new User().setId( "A" ).setName( "张三" )); userList.add( new User().setId( "A" ).setName( "李四" )); //相同的key userList.add( new User().setId( "C" ).setName( "王五" )); userList.stream().collect(Collectors.toMap(User::getId, User::getName)); // 异常: java.lang.IllegalStateException: Duplicate key 张三 at java.util.stream.Collectors.lambda$throwingMerger$ 114 (Collectors.java: 133 ) at java.util.HashMap.merge(HashMap.java: 1245 ) at java.util.stream.Collectors.lambda$toMap$ 172 (Collectors.java: 1320 ) at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java: 169 ) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java: 1374 ) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java: 481 ) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java: 471 ) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java: 708 ) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java: 234 ) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java: 499 ) at Test.toMap(Test.java: 17 ) ... |
这时就需要调用第二个重载方法,传入合并函数,如:
1 2 3 4 5 | userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2)); // 输出结果: A-> 张三李四 C-> 王五 |
第四个参数mapSupplier
用于返回一个任意类型的Map实例,比如我们希望返回的Map
是根据 Key
排序的,可以使用如下写法:
1 2 3 4 5 6 7 8 9 10 11 12 | List<User> userList = Lists.newArrayList( new User().setId( "B" ).setName( "张三" ), new User().setId( "A" ).setName( "李四" ), new User().setId( "C" ).setName( "王五" ) ); userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1, TreeMap:: new )); //输出结果: A-> 李四 B-> 张三 C-> 王五 |
或者转化为ConcurrentMap
:
1 2 3 | userList.stream().collect( Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1, ConcurrentHashMap:: new ) ); |
注意事项
在使用java.util.stream.Collectors
类的 toMap()
方法转为 Map
集合时,一定要注
意当value
为 null
时会抛 NPE
异常。
在 java.util.HashMap
的 merge
方法里会进行如下的判断:
1 2 | if (value == null || remappingFunction == null ) throw new NullPointerException(); |
三丶总结
在本文中我们快速的学习了Collectors.toMap()
方法的使用,它允许我们创建一个新的Map
。我们还学习了如果解决Key
冲突以及如何返回不同类型的Map
还有value
为时null
的处理方式。
原文地址:https://www.jianshu.com/p/b2d78544df64
作者:假装空白
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人