Collectors.toMap

假设需求时这样的

将一个对象list转换成map

// 原数据:
List<Order> orders;

// 转换后数据:
Map<Long, String> orderMaps;

list里的元素


image.png

转换后map的key-value


image.png

现阶段的解决方式一般有2大类

  1. java代码里显示使用for循环
  2. 通过stream的内循环

先写简单的方案,显示for循环如下

// 我不写了,这个太简单了

再来写stream的方案

  1. 通过集合类型的.stream()函数将集合转成流,
  2. 然后通过collect()函数收集流数据,
  3. 此函数的参数是Collectors.toXXX();
    代码如下:
准备工作1,这是模拟的订单类,凑合一下
/**
 * 模拟订单类
 */
@Data
@AllArgsConstructor
@Accessors(chain = true)
class Order {
    private Long id;
    private String receiverName;
}
准备工作2,准备一个订单集合
static List<Order> buildOrders() {
        Order order1 = new Order(1L, "李静");
        Order order2 = new Order(2L, "沙坪");
        Order order3 = new Order(3L, "老李");

        List<Order> orders = new ArrayList<>(3);
        orders.add(order1);
        orders.add(order2);
        orders.add(order3);

        return orders;
    }

处理工作,将list转成map

先来写个简易版本
// 方式1, 简易版
        Map<Long, String> map2 = orders.stream().collect(Collectors.toMap(
                kv -> { // 处理key的函数
                    return kv.getId();
                }, 
                kv -> { // 处理value的函数
                    return kv.getReceiverName();
                }));
        // 观察返回map的类型
        System.out.println(map2.getClass());
        map2.forEach((k, v) -> {
            System.out.println(k + "-" + v);
        });

执行结果如下:可以发现返回map的类型是java.util.HashMap

class java.util.HashMap
1-李静
2-沙坪
3-老李

再来写一个进阶版本,这是看阿里开发手册发现的

因为原集合转换成map的过程中,有可能会出现key重复,为了解决key重复,所以Collectors.toMap函数提供了重载方法,第三个参数函数用于处理key重复

方式2,进阶版
Map<Long, String> map3 = orders.stream().collect(Collectors.toMap(
                kv -> {
                    return kv.getId();
                },
                kv -> {
                    return kv.getReceiverName();
                },
                (oldKey, newKey) -> { // 处理key重复的函数
                    System.err.println("key重复,已替换");
                    return newKey;
                }));
        System.out.println(map3.getClass());
        map3.forEach((k, v) -> {
            System.out.println(k + "-" + v);
        });

执行之前做下修改:
这里需要先将原集合,即order集合里的元素值改成如下内容,order2与order3的id都是2L,这样会出现key重复

        Order order1 = new Order(1L, "李静");
        Order order2 = new Order(2L, "沙坪");
        Order order3 = new Order(2L, "老李");

执行结果如下:

  1. 简易版的执行结果:很显然的报出了key重复的异常

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 沙坪
at java.util.stream.Collectors.lambdathrowingMerger0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
at java.util.stream.Collectors.lambdatoMap58(Collectors.java:1320)
at java.util.stream.ReduceOps3ReducingSink.accept(ReduceOps.java:169) at java.util.ArrayListArrayListSpliterator.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 com.enn.sale.crm.clue.web.Test.main(Test.java:39)

  1. 进阶版本执行结果:没啥问题,重复的key已经按照代码内容处理了,返回map类型依然是java.util.HashMap

class java.util.HashMap
1-李静
2-老李

再写一个增强版本吧

这个版本的目的是,指定转换后map类型,比如我不想用HashMap,我可以指定为TreeMap

Map<Long, String> map4 = orders.stream().collect(Collectors.toMap(
                kv -> {
                    return kv.getId();
                },
                kv -> {
                    return kv.getReceiverName();
                },
                (oldKey, newKey) -> {
                    System.err.println("key重复,已替换");
                    return newKey;
                },
                () -> {
                    // 此处是个提供者类型的函数式接口,不用参数,只返回
                    // 返回值为指定转换后map的类型的对象
                    // 源码注释是这样的,自己体会吧
                    // a function which returns a new, empty {@code Map} into
                    // * which the results will be inserted
                    return new TreeMap<>();
                }
        ));

        System.out.println(map4.getClass());
        map4.forEach((k, v) -> {
            System.out.println(k + "-" + v);
        });

执行结果如下:返回类型已经是java.util.TreeMap

class java.util.TreeMap
1-李静
2-老李

本篇内容基本上写完了,建议在开发中尽量不要使用简易版本,key重复这种情况很难避免

粘贴下1.8的部分源码

/**
     * @param keyMapper a mapping function to produce keys
     * @param valueMapper a mapping function to produce values
     * @param mergeFunction a merge function, used to resolve collisions between
     *                      values associated with the same key, as supplied
     *                      to {@link Map#merge(Object, Object, BiFunction)}
     * @param mapSupplier a function which returns a new, empty {@code Map} into
     *                    which the results will be inserted
     * @return a {@code Collector} which collects elements into a {@code Map}
     * whose keys are the result of applying a key mapping function to the input
     * elements, and whose values are the result of applying a value mapping
     * function to all input elements equal to the key and combining them
     * using the merge function
     *
     * @see #toMap(Function, Function)
     * @see #toMap(Function, Function, BinaryOperator)
     * @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier)
     */
    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)

写完收工

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容