解决使用stream将list转map时,key重复导致报错的问题

要将List对象集合转为map集合,可以通过stream流的形式快速实现转换:

//三个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()方法:

//三个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:

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);
    }

其在转换过程中,会抛出异常:

 @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));
    }

抛出异常信息:

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情况下的冲突处理策略:

 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));

这里的处理策略是:

(k1, k2) -> k1

总结

在Collectors.toMap()转换过程中,需要注意一下duplicate key的处理逻辑,需要增加mergeFunction()处理方法。以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们

时间: 2021-06-09

Java lambda list转换map时,把多个参数拼接作为key操作

我就废话不多说了,大家还是直接看代码吧~ Map<String, Parts> partsMap = synList.stream().collect(Collectors.toMap(k -> k.getOe()+k.getOeId()+k.getPartGroupId()+k.getStdPartId()+k.getBrandCode(), part -> part)); 补充知识:Java8 Collectors.toMap的两个大坑 Collectors.toMap()方法

在Java 8中将List转换为Map对象方法

假设有一个员工对象: <b>public</b> <b>class</b> Employee { <font><i>// member variables</i></font><font> <b>private</b> <b>int</b> empId; <b>private</b> String empName; <b&

java8快速实现List转map 、分组、过滤等操作

利用java8新特性,可以用简洁高效的代码来实现一些数据处理. 定义1个Apple对象: public class Apple { private Integer id; private String name; private BigDecimal money; private Integer num; public Apple(Integer id, String name, BigDecimal money, Integer num) { this.id = id; this.name =

详解Java8新特性Stream之list转map及问题解决

List集合转Map,用到的是Stream中Collectors的toMap方法:Collectors.toMap 具体用法实例如下: //声明一个List集合 List<Person> list = new ArrayList(); list.add(new Person("1001", "小A")); list.add(new Person("1002", "小B")); list.add(new Person

详解Java8新特性之interface中的static方法和default方法

为什么要单独写个Java8新特性,一个原因是我目前所在的公司用的是jdk8,并且框架中用了大量的Java8的新特性,如上篇文章写到的stream方法进行过滤map集合.stream方法就是接口Collection中的default方法.所以准备专门写写关于java8新特性的文章,虽然现在10已经发布了.但还是要认真的去了解下新版本的变化. static方法 java8中为接口新增了一项功能:定义一个或者更多个静态方法.用法和普通的static方法一样. 代码示例 public interface

详解Java8 新特性之日期API

Java 8 在包java.time下包含了一组全新的时间日期API.下面的例子展示了这组新API里最重要的一些部分: 1.Clock 时钟 Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数.某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象. Clock clock = Clock.systemDefaultZone();

Java8新特性Stream流实例详解

什么是Stream流? Stream流是数据渠道,用于操作数据源(集合.数组等)所生成的元素序列. Stream的优点:声明性,可复合,可并行.这三个特性使得stream操作更简洁,更灵活,更高效. Stream的操作有两个特点:可以多个操作链接起来运行,内部迭代. Stream可分为并行流与串行流,Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换.串行流就不必再细说了,并行流主要是为了为了适应目前多核机器的时代,提高系统CP

Java8新特性Stream短路终端操作实例解析

这篇文章主要介绍了Java8新特性Stream短路终端操作实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 传入一个谓词,返回传为boolean,如果符合条件,则直接结束流. 匹配所有 allMatch 任意匹配 anymMatch 不匹配 noneMatch 查找首个 findFirst 查找任意 findAny 匹配所有 allMatch /匹配所有 allMatch @Test public void allMatchTest()

Java8新特性Stream的完全使用指南

什么是Stream Stream是Java 1.8版本开始提供的一个接口,主要提供对数据集合使用流的方式进行操作,流中的元素不可变且只会被消费一次,所有方法都设计成支持链式调用.使用Stream API可以极大生产力,写出高效率.干净.简洁的代码. 如何获得Stream实例 Stream提供了静态构建方法,可以基于不同的参数创建返回Stream实例 使用Collection的子类实例调用stream()或者parallelStream()方法也可以得到Stream实例,两个方法的区别在于后续执行

java8新特性 stream流的方式遍历集合和数组操作

前言: 在没有接触java8的时候,我们遍历一个集合都是用循环的方式,从第一条数据遍历到最后一条数据,现在思考一个问题,为什么要使用循环,因为要进行遍历,但是遍历不是唯一的方式,遍历是指每一个元素逐一进行处理(目的),而并不是从第一个到最后一个顺次处理的循环,前者是目的,后者是方式. 所以为了让遍历的方式更加优雅,出现了流(stream)! 1.流的目的在于强掉做什么 假设一个案例:将集合A根据条件1过滤为子集B,然后根据条件2过滤为子集C 在没有引入流之前我们的做法可能为: public cl

详解Java8 Collect收集Stream的方法

Collection, Collections, collect, Collector, Collectos Collection是Java集合的祖先接口. Collections是java.util包下的一个工具类,内涵各种处理集合的静态方法. java.util.stream.Stream#collect(java.util.stream.Collector<? super T,A,R>)是Stream的一个函数,负责收集流. java.util.stream.Collector 是一个收

详解java8中的Stream数据流

Stream是java8引入的一个重度使用lambda表达式的API.Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象.直观意味着开发者在写代码时只需关注他们想要的结果是什么而无需关注实现结果的具体方式.这一章节中,我们将介绍为什么我们需要一种新的数据处理API.Collection和Stream的不同之处以及如何将StreamAPI应用到我们的编码中. 筛选重复的元素 Stream 接口支持 distinct 的方法, 它会返回一个元素

详解IOS11新特性之larget title的实现

本文介绍了IOS11新特性之larget title的实现,分享给大家,具体如下: 大标题(larget title) 图层解析 小标题所处.jpg 这就是我们平常所见的Nav的title 大标题所处.jpg 这是IOS11新特性larget title 处于哪里.jpg 他们其实都在navigationBar这个view上,但是大标题先添加在navigationBar上的,看下图便知道了 上拉到顶部.png 只不过是小标题把大标题遮盖住了 //必须要设置navigationBar的prefer