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开发手册中也明确指出: