Java - Collectors.toMap() 使用

List 转 Map

一、Collectors.toMap() 函数源码定义:

	// 两个参数
    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);
    }
	
	// 三个参数
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
        return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
    }
    
    // 四个参数
    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);
    }

参数含义分别是:

  • keyMapper:Key 的映射函数。
  • valueMapper:Value 的映射函数。
  • mergeFunction:当 Key 冲突时,调用的合并方法。
  • mapSupplier:Map 构造器,在需要返回特定的 Map 时使用。

二、使用方法

List<People> arrayList = new ArrayList<>();
arrayList.add(new People("张三", "上海", "演员"));
arrayList.add(new People("李四", "北京", "IT"));
arrayList.add(new People("王五", "深圳", "医生"));
arrayList.add(new People("张三", "成都", "律师"));

1.name作为key,整个People对象作为value

Map<String, People> map = arrayList.stream().collect(Collectors.toMap(People::getName, a -> a, (oldV, newV) -> newV));
System.out.println(map);

// 运行结果
{李四=People{name='李四', city='北京', job='IT'}, 张三=People{name='张三', city='成都', job='律师'}, 王五=People{name='王五', city='深圳', job='医生'}}

这里使用传入 mergeFunction 参数,当 Key 冲突时,调用的合并方法。当Key出现重时如果想使用新值使用 (oldV, newV) -> newV,如果想使用老值使用 (oldV, newV) -> oldV

如果不传 mergeFunction 参数,结果会报错:

Map<String, People> map = arrayList.stream().collect(Collectors.toMap(People::getName, a -> a));

// 报错结果
Exception in thread "main" java.lang.IllegalStateException: Duplicate key People{name='张三', city='上海', job='演员'}
	at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
	at java.util.HashMap.merge(HashMap.java:1255)
	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:1384)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	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.example.Main.collectorsToMap(Main.java:132)
	at org.example.Main.main(Main.java:69)

所以除非你能确保Key不会出现重复(这个很难保证),否则就需要传入 mergeFunction 参数,确定value合并策略。

2.name作为key,整个job作为value

Map<String, String> map3 = arrayList.stream().collect(Collectors.toMap(People::getName, People::getJob, (oldV, newV) -> newV));
System.out.println(map3);

// 运行结果
{李四=IT, 张三=律师, 王五=医生}

如果我们将数据改下,再加一条数据

arrayList.add(new People("张三", "云南", null));

Map<String, String> map3 = arrayList.stream().collect(Collectors.toMap(People::getName, People::getJob, (oldV, newV) -> newV));

// 运行报错
Exception in thread "main" java.lang.NullPointerException
	at java.util.HashMap.merge(HashMap.java:1226)
	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:1384)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	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.example.Main.collectorsToMap(Main.java:135)
	at org.example.Main.main(Main.java:69)

我们要确保value不能null,所以需要加安全判断:

Map<String, String> map3 = arrayList.stream().collect(Collectors.toMap(People::getName, a -> a.getJob() == null ? "" : a.getJob(), (oldV, newV) -> newV));

3.假如存在name重复,两个vaue可以这样映射到同一个name

Map<String, String> map3 = arrayList.stream().collect(Collectors.toMap(People::getName, a -> a.getJob() == null ? "" : a.getJob(), (oldV, newV) -> (newV + "," + oldV)));
System.out.println(map3);
       
// 运行结果
{李四=IT, 张三=律师,演员, 王五=医生} 

4.把People集合按照group分组到map中

需要注意的是group分组后value对应的是个数组

Map<String, List<People>> map = arrayList.stream().collect(Collectors.groupingBy(People::getName));
System.out.println(map);

// 运行结果
{李四=[People{name='李四', city='北京', job='IT'}], 张三=[People{name='张三', city='上海', job='演员'}, People{name='张三', city='成都', job='律师'}], 王五=[People{name='王五', city='深圳', job='医生'}]}

三、注意点

使用 Collectors.toMap 使用起来确实方便,但也要注意几点:

1.需要考虑key是否为null情况;
2.需考虑是否有key重复情况;
3.需考虑是否有value为null情况。

阿里的Java开发手册中也明确指出:

在这里插入图片描述

java stream Collectors.toMap是一个流式处理方法,用于将一个List集合转换为Map集合。它接受三个参数:第一个参数指定了作为Map的key值的属性或方法引用,第二个参数指定了作为Map的value值的属性或方法引用,第三个参数用于处理key重复的情况。 通过使用Collectors.toMap方法,可以将一个List集合中的元素按照指定的属性或方法引用作为key值,将整个对象或属性值作为value值,构建出一个key值唯一的Map集合。 举例来说,如果有一个List<proVo>对象的集合,我们希望将其中的proVo对象按照id作为key,name作为value,构建成一个Map集合,可以使用如下代码: Map<Long, String> map = list.stream().collect(Collectors.toMap(proVo::getId, proVo::getName)); 这样就可以得到一个key为id,value为name的Map集合。 另外,如果在转换过程中遇到了key重复的情况,可以通过第三个参数来处理。比如可以选择保留前一个元素的值,或者保留后一个元素的值,或者进行其他自定义的处理。 例如: Map<Long, String> map = list.stream().collect(Collectors.toMap(proVo::getId, proVo::getName, (k1, k2) -> k1)); 这样当遇到key重复的情况时,会保留前一个元素的值作为value。 总之,java stream Collectors.toMap方法是用于将List集合转换为Map集合的一个便捷方法,可以根据需求选择合适的参数来进行处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java8stream源码-streams:用于针对旧设备的Android项目的java.util.stream库的非常不完整的重新实现](https://download.csdn.net/download/weixin_38690522/19389466)[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: 50%"] - *2* *3* [【业务功能篇35】Java Stream流 :Collectors.toMap](https://blog.csdn.net/studyday1/article/details/131454535)[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: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值