Collectors.toMap 使用技巧
前言
-
过去的做法(List 转 Map)
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()); } -
如今我们可以利用Java8 中新增的 Stream 特性,使得操作集合更加的方便
使用Collectors.toMap来代替之前的手写遍历,但是在阅读Java开发手册(嵩山版)后,发现了一个容易被忽视的隐患!
原文如下:
【强制】在使用java.util.stream.Collectors类的toMap()方法转为 Map 集合时,一定要使用含有参数类型为BinaryOperator,参数名为mergeFunction的方法,否则当出现相同key值时会抛出IllegalStateException异常。
说明:参数mergeFunction的作用是当出现key重复时,自定义对value的处理策略。
正例:
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));
反例:
String[] departments = new String[] {"iERP", "iERP", "EIBU"};
// 抛出 IllegalStateException 异常
Map<Integer, String> map = Arrays.stream(departments)
.collect(Collectors.toMap(String::hashCode, str -> str));
一、Collectors.toMap的详细使用技巧
-
开篇遍历的做法, 我们可以一行代码搞定:
userList.stream().collect(Collectors.toMap(User::getId, User::getName)); -
当然,如果希望得到
Map的value为对象本身时,可以这样写:userList.stream().collect(Collectors.toMap(User::getId, t -> t)); 或者: userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
关于 Collectors.toMap 方法
-
Collectors.toMap有三个重载方法:
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 有相同的,使用上面的写法会抛异常:
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) ...这时就需要调用第二个重载方法,传入合并函数,如:
userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2)); // 输出结果: A-> 张三李四 C-> 王五第四个参数
mapSupplier用于返回一个任意类型的Map实例,比如我们希望返回的Map是根据 Key 排序的,可以使用如下写法: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: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 方法里会进行如下的判断:
if (value == null || remappingFunction == null)
throw new NullPointerException();
二、总结
在本文中我们快速的学习了Collectors.toMap()方法的使用,它允许我们创建一个新的Map。我们还学习了如果解决Key冲突以及如何返回不同类型的Map还有value为时null的处理方式。
本文介绍了Java 8中Stream.Collectors.toMap的使用技巧,强调了在转换Map时处理Key冲突的重要性,并提供了示例说明如何自定义合并策略。同时,展示了如何通过指定MapSupplier返回特定类型的Map,以及处理value为null的情况。
1万+

被折叠的 条评论
为什么被折叠?



