解决使用stream将list转map时,key重复导致报错的问题
要将List对象集合转为map集合,可以通过stream流的形式快速实现转换:
1 2 3 4 5 6 7 8 9 | //三个Users对象组成一个List集合 List<Users> list = new ArrayList<>(); list.add(Users.builder().userName( "11" ).userId( 1 ).build()); list.add(Users.builder().userName( "11" ).userId( 2 ).build()); list.add(Users.builder().userName( "33" ).userId( 3 ).build()); //将list转map Map<String, Users> usersMap = list.stream() .collect(Collectors.toMap(Users::getUserName, user -> user)); System.out.println(usersMap.get( "11" )); |
但是上述代码运行后报了异常:
意思为map中出现了重复的key,也就是说通过上述方法转map时,出现重复key并不会出现覆盖的情况,而是再次在map中添加一个重复的key,导致报错。
所以通过stream实现list转map时,要实现重复的key会被覆盖,可以使用Function.identity()方法:
1 2 3 4 5 6 7 8 9 10 11 | //三个Users对象组成一个List集合 List<Users> list = new ArrayList<>(); list.add(Users.builder().userName( "11" ).userId( 1 ).build()); list.add(Users.builder().userName( "11" ).userId( 2 ).build()); list.add(Users.builder().userName( "33" ).userId( 3 ).build()); //将list转map,这里是出现重复key时,覆盖前一个 Map<String, Users> usersMap = list.stream() .collect(Collectors.toMap(Users::getUserName, Function.identity(), (user1, user2) -> user2)); System.out.println(usersMap.get( "11" )); //输出结果: edu.nf.ch08.entity.Users @41aaedaa |
JDK 8 Stream List转换为Map的duplicate Key异常
Stream List to Map
Stream提供了List转换为Map提供了非常易用的方法:
Collectors.java:
1 2 3 4 5 | public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap:: new ); } |
其在转换过程中,会抛出异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Test (expected = IllegalStateException. class ) public void testStreamMap_duplicateKey() { Employee employee = Employee.builder().id( 1 ).age( 20 ).firstName( "zhang" ).build(); Employee employee1 = Employee.builder().id( 2 ).age( 21 ).firstName( "Li" ).build(); Employee employee2 = Employee.builder().id( 3 ).age( 22 ).firstName( "Li" ).build(); Employee employee3 = Employee.builder().id( 4 ).age( 23 ).firstName( "Chen" ).build(); List<Employee> employees = Lists.newArrayList(); employees.add(employee); employees.add(employee1); employees.add(employee2); employees.add(employee3); Map<String, Integer> dataMap = employees.stream().collect(Collectors.toMap(e -> e.getFirstName(), e -> e.getAge())); //Duplicate Key Map<Integer, Employee> employeeMap = employees.stream().collect(Collectors.toMap(e->e.getAge(), e->e)); } |
抛出异常信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | java.lang.IllegalStateException: Duplicate key 21 at java.util.stream.Collectors.lambda$throwingMerger$ 0 (Collectors.java: 133 ) at java.util.HashMap.merge(HashMap.java: 1254 ) at java.util.stream.Collectors.lambda$toMap$ 58 (Collectors.java: 1320 ) at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java: 169 ) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java: 1382 ) 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 org.cjf.java.learn.jdk8.StreamTest.testStreamMap_duplicateKey(StreamTest.java: 90 ) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 62 ) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 43 ) at java.lang.reflect.Method.invoke(Method.java: 498 ) at org.junit.runners.model.FrameworkMethod$ 1 .runReflectiveCall(FrameworkMethod.java: 50 ) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java: 12 ) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java: 47 ) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java: 17 ) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java: 325 ) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java: 78 ) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java: 57 ) at org.junit.runners.ParentRunner$ 3 .run(ParentRunner.java: 290 ) at org.junit.runners.ParentRunner$ 1 .schedule(ParentRunner.java: 71 ) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java: 288 ) at org.junit.runners.ParentRunner.access$ 000 (ParentRunner.java: 58 ) at org.junit.runners.ParentRunner$ 2 .evaluate(ParentRunner.java: 268 ) at org.junit.runners.ParentRunner.run(ParentRunner.java: 363 ) at org.junit.runner.JUnitCore.run(JUnitCore.java: 137 ) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java: 68 ) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java: 33 ) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java: 230 ) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java: 58 ) Process finished with exit code 255 |
如何解决
增加重复key情况下的冲突处理策略:
1 2 3 4 5 6 7 | Map<String, Integer> dataMap = employees.stream().collect(Collectors.toMap(e -> e.getFirstName(), e -> e.getAge(), (k1, k2)-> k1)); Assert.assertThat(dataMap, hasKey( "zhang" )); Assert.assertThat(dataMap, hasKey( "Li" )); Assert.assertThat(dataMap, hasKey( "Chen" )); Assert.assertThat(dataMap.keySet(), hasSize( 3 )); Map<Integer, Employee> employeeMap = employees.stream().collect(Collectors.toMap(e->e.getAge(), e->e, (k1, k2) -> k1)); Assert.assertThat(dataMap.keySet(), hasSize( 3 )); |
这里的处理策略是:
总结
在Collectors.toMap()转换过程中,需要注意一下duplicate key的处理逻辑,需要增加mergeFunction()处理方法。以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家