• Collectors.toMap()方法——Java8


    1. Collectors.toMap() 输出乱序

    1.1 场景
    想按创建时间降序列表展示订单信息,但最终返回给前端的数据和idList顺序不一致,乱序输出。
    Debug发现有段代码,根据idList从数据库中查询出orderList,输出一个以订单编号为key,订单内容为value的Map,该Map输出内容是乱序的。

    ......
    //根据订单idList查询订单列表
    List orderList = orderDao.listOrderByIds(idList);  
    //输出以订单id为key的Map
    Map orderMap = orders.stream().collect(Collectors.toMap(Order::getId, order -> order, (k1, k2) -> k1));
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查看Collectors.toMap()源码发现其输出的Map是HashMap,而HashMap不是按顺序存的。

    Collectors.toMap()方法源码:

    2,Collectors.toMap()有三个重载方法:

    第一个默认生成HashMap:

    public static 
        Collector> toMap(Function keyMapper,
                                        Function valueMapper) {
            return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第二个默认生成也是一个HashMap:

    public static 
        Collector> toMap(Function keyMapper,
                                        Function valueMapper,
                                        BinaryOperator mergeFunction) {
            return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第二个和第一个的区别在于是否默认抛出异常:

    原文:Java8 Collectors.toMap的坑_八行书-CSDN博客_java8 tomap按照常规思维,往一个map里put一个已经存在的key,会把原有的key对应的value值覆盖,然而通过一次线上问题,发现Java8中的Collectors.toMap反其道而行之,它默认给抛异常,抛异常…?线上业务代码出现Duplicate Key的异常,影响了业务逻辑,查看抛出异常部分的代码,类似以下写法:Map map = li…[这里是图片002]https://blog.csdn.net/u013805360/article/details/82686009

    按照常规思维,往一个map里put一个已经存在的key,会把原有的key对应的value值覆盖。

    然而第一个toMap()方法反其道而行之,它默认给抛异常…

    查看源码发现这个toMap方法默认使用了个throwingMerger,后边传进去的Map类型又写死是一个HashMap,所以最终走的是HashMap的merge方法。merge方法里面有这么一段代码:

    if (old != null) {
        V v;
        if (old.value != null)
            v = remappingFunction.apply(old.value, value);
        else
            v = value;
        if (v != null) {
            old.value = v;
            afterNodeAccess(old);
        }
        else
            removeNode(hash, key, null, false, true);
        return v;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    只看变量名就能知道这段代码啥意思了。。如果要put的key已存在,那么就调用传进来的方法。而throwingMerger的做法就是抛了个异常。。。

    如果不想抛异常的话,自己传进去一个方法即可,上述代码可以改成:

    Map map = list.stream().collect(Collectors.toMap(Person::getId, Person::getName,(oldValue, newValue) -> newValue));
    
    • 1

    在这里自己传进去一个方法之后,调用的就是第二个toMap方法了,第二个方法,通过传参来自定义对key发生冲突的处理了。

    第三个可以自定义异常方法和Map类型:

    public static >
        Collector toMap(Function keyMapper,
                                    Function valueMapper,
                                    BinaryOperator mergeFunction,
                                    Supplier mapSupplier) {
            BiConsumer accumulator
                    = (map, element) -> map.merge(keyMapper.apply(element),
                                                  valueMapper.apply(element), mergeFunction);
            return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3. 解决toMap()乱序:使用LinkedHashMap

    如果想要输出有序,推荐使用LinkedHashMap

    LinkedHashMap除实现HashMap,还维护了一个双向链表。LinkedHashMap为每个Entry添加了前驱和后继,每次向linkedHashMap插入键值对,除了将其插入到哈希表的对应位置之外,还要将其插入到双向循环链表的尾部。
    在循环遍历时,HashMap遍历的是tab[]数组,LinkedHashMap遍历的是双向链表,从head开始,即最初的链表顺序。

    为保证输出有序,选择LinkedHashMap,具体修改方案如下:

    LinkedHashMap orderMap = orders.stream().collect(Collectors.toMap(Order::getId, order -> order, (oldData, newData) -> newData,LinkedHashMap::new));
    
    • 1



    4. Collectors.groupingBy() 输出乱序

    4.1 场景

    想要以id分组输出Map,结果输出是乱序的。

    Map> map;
    map = list.stream().collect(Collectors.groupingBy(OrderVO::getId));
    
    • 1
    • 2

    其原因和Collectors.toMap()类似,查看源码可知Collectors.groupingBy()输出为HashMap。

    public static 
    	Collector> groupingBy(Function classifier,
    								  Collector downstream) {
    		return groupingBy(classifier, HashMap::new, downstream);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2 解决方案

    选择以LinkedHashMap输出。

    map = list.stream().collect(Collectors.groupingBy(OrderVO::getId, LinkedHashMap::new, Collectors.toList()));
    
    • 1
  • 相关阅读:
    Java Double toString()方法具有什么功能呢?
    “Sky Walker Home” 春节大促活动推广方案设计
    单个人工神经元模型示意图,人体神经元模型制作
    监控web项目都访问了那些网址
    小程序全局变量与组件内部实时监听全局变量的改变
    大数据学习(11)-hive on mapreduce详解
    python使用mitmproxy和mitmdump抓包在手机上抓包(三)
    SpringBoot集成WebSocket--------Spring方式集成(一)
    什么是自主系统?
    Makefile template
  • 原文地址:https://blog.csdn.net/web17886480312/article/details/126326949