Java8 Collectors.toMap Duplicate key 报错问题。

在使用java8 中Collectors.toMap的时候出现了异常,具体异常如下:

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 

该异常字面意思是有重复的key,但是使用 Collectors.toMap 表示将数据集合转换为map,map的key如果出现hash冲突则会覆盖,不明所以写了一个main方法测试。

    @Data
    public static class Student{
        private Integer id;
        private String name;
    }
    public static void main(String[] rags){
        //2个student对象 有id,name属性。相同ID 不同的name,看是否会出现重复keyY异常。
        List<Student> studentList = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(2);//重复ID 
        student1.setName("a");
        Student student2 = new Student();
        student2.setId(2);//重复ID 
        student2.setName("b");
        studentList.add(student1);
        studentList.add(student2);

        Map<Integer, Student> map1 = studentList.stream().collect(Collectors.toMap(Student::getId, Student::getName));
        System.out.println(map1);
    }

执行结果:


Exception in thread "main" java.lang.IllegalStateException: Duplicate key a
	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)

果然出现了重复key冲突的异常,进入源码跟踪。

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

toMap2个入参一个keyMapper,一个valueMapper,但是出来一个throwingMerger()?先不管继续进入toMap方法查看。

    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                Function<? super T, ? extends U> valueMapper,
                                BinaryOperator<U> mergeFunction,
                                Supplier<M> mapSupplier) {
        BiConsumer<M, T> accumulator
                = (map, element) -> map.merge(keyMapper.apply(element),
                                              valueMapper.apply(element), mergeFunction);
        return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
    }

 进入toMap方法没有太多逻辑执行的是map的merge方法。继续进入查看。

    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);//非空判断 外面肯定传入了
        Objects.requireNonNull(value);//value 也不能为空?
        V oldValue = get(key);//查找有这个key是否已经有值,返回值声明为oldValue
        //关键点,如果oldValue==null使用新value,有值则直接执行传入的函数
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);

        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }

如代码注释中描述,

1.查找有这个key是否已经有值,返回值声明为oldValue
2. 如果oldValue==null使用新value,有值则直接执行传入的函数

3.value不能为空,否则抛出空指针异常。

传入的函数就是刚开始toMap的时候看到的throwingMerger()方法,所以现在进入看下是干什么的。

   private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

结合该函数调用apply的时候remappingFunction.apply(oldValue, value),可以理解为

就是抛出一个异常并且告诉你那个key重复了。。。。

一时间让我对map可以重复key有所误解还是java8对map有什么误解有所怀疑。

再回到最初toMap的方法上查看注释

  * <p>If the mapped keys contains duplicates (according to
     * {@link Object#equals(Object)}), an {@code IllegalStateException} is
     * thrown when the collection operation is performed.  If the mapped keys
     * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)}
     * instead.
    //如果映射key包含重复的,则会抛出IllegalStateException,
     * @apiNote
     * It is common for either the key or the value to be the input elements.
     * In this case, the utility method
     * {@link java.util.function.Function#identity()} may be helpful.
     * For example, the following produces a {@code Map} mapping
     * students to their grade point average:
     * <pre>{@code
     *     Map<Student, Double> studentToGPA
     *         students.stream().collect(toMap(Functions.identity(),
     *                                         student -> computeGPA(student)));
     * }</pre>
     * And the following produces a {@code Map} mapping a unique identifier to
     * students:
     * <pre>{@code
     *     Map<String, Student> studentIdToStudent
     *         students.stream().collect(toMap(Student::getId,
     *                                         Functions.identity());
     * }</pre>
     *

翻一下大致为如果映射key包含重复的,则会抛出IllegalStateException,通过函数编程。

而紧接着又给出了该情况下的解决办法,可以我们传入一个对比函数进行处理发生merge的情况。

所以代码变更为:


Map<Integer, String> map1 = studentList.stream().collect(Collectors.toMap(Student::getId,Student::getName ,(s,s2)->s2));
        System.out.println(map1);//{2=b}

所以在出现重复key的时候对应mergeFunction 就是我们toMap中第三个参数的函数。

查了下openJDK,这个问题之前有人提过bug,并且在JDK1.9修复了。

详见[JDK-8040892] Incorrect message in Exception thrown by Collectors.toMap(Function,Function) - Java Bug System

总结:
1.使用toMap的时候如果会有重复key的情况要自己指定处理方式不然会报错。
2.toMap不允许空value.
使用任何新功能前还是要先看文档呀。

 关注我的公众号 LearnMoreDoMore 可以及时看到更多技术分享文章(不会该死的卖课广告)。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Collectors.toMap方法在使用时,如果遇到重复的key会抛出DuplicateKey异常。这意味着在生成Map时,已经存在相同的key,导致无法插入新的键值对。为了解决这个问题,可以使用Collectors.toMap方法的重载版本,提供一个mergeFunction参数来处理重复的key。mergeFunction是一个BinaryOperator函数,用于定义如何合并重复的key对应的value值。在合并时,可以根据具体的业务逻辑来决定保留旧值还是使用新值。通过使用mergeFunction,可以解决DuplicateKey异常的问题。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JDK8中Collectors.toMap方法报Duplicatkey xxx错误信息](https://blog.csdn.net/qq_19734597/article/details/86087489)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [ListToMapDuplicateKey.java](https://download.csdn.net/download/snxkxk/12321923)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java8 Collectors.toMap Duplicate key 报错问题。](https://blog.csdn.net/Etoak_james/article/details/121234896)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值