Java8中使⽤Stream让List转Map使⽤问题⼩结
在使⽤ Java 的新特性 Map() 将 List 转换为 Map 时存在⼀些不容易发现的问题,这⾥总结⼀下备查。
空指针风险
java.lang.NullPointerException
当 List 中有 null 值的时候,使⽤ Map() 转为 Map 时,会报 java.lang.NullPointerException,如下:
List<SdsTest> sdsTests = new ArrayList<>();
SdsTest sds1 = new SdsTest("aaa","aaa");
SdsTest sds2 = new SdsTest("bbb",null);
sdsTests.add(sds1);
sdsTests.add(sds2);
Map<String, String> map = sdsTests.stream().Map(SdsTest::getName, SdsTest::getAge));
System.out.String());
---------
运⾏错误:
Exception in thread "main" java.lang.NullPointerException
at java.(HashMap.java:1216)
at java.util.stream.Collectors.lambda$toMap$150(Collectors.java:1320)
.....
  原因是toMap()⽅法中使⽤()⽅法合并时,merge 不允许 value 为 null 导致的,源码如下:
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
{
// 在这⾥判断了value不可为null
V oldValue = get(key);
V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
...
解决⽅法
业务控制不要出现 Null 值【有 Null 的地⽅,可以赋值默认值】在转换时加判断,如果为 null,则给⼀个默认值
Map<String, String> map = sdsTests.stream().Map(SdsTest::getName, sdsTest -> Age() == null ? "0" : Age()));使⽤ collect(..) 构建,允许空值
Map<String, String> nmap = sdsTests.stream().collect(HashMap::new,(k, v) -> k.Name(), v.getAge()), HashMap::putAll);
// TODO 下游业务从Map取值要做NPE判断
使⽤ Optional 对值进⾏包装
Map<String, Optional<String>> opmap = sdsTests.stream().Map(SdsTest::getName, sdsTest -> Optional.Age()))); System.out.println("bbb.age=" + ("bbb").orElse("0"));
------------
输出:
bbb.age=0
建议
优先业务控制,尽量避免 List 中存在 Null
其次推荐第 4 种⽅法【使⽤ Optional 对值进⾏包装】,能很好的避免 NPE 问题
key重复风险
java.lang.IllegalStateException: Duplicate key xx
当 List 中有重复值的时候,使⽤ Map() 转为 Map 时,会报:java.lang.IllegalStateException: Duplicate key xx,例如
List<SdsTest> sdsTests = new ArrayList<>();
SdsTest sds1 = new SdsTest("aaa","aaa");
SdsTest sds2 = new SdsTest("aaa","ccc");
sdsTests.add(sds1);
sdsTests.add(sds2);
Map<String, String> map = sdsTests.stream().Map(SdsTest::getName, SdsTest::getAge));
System.out.String());
---------
运⾏错误:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key aaa
at java.util.stream.Collectors.lambda$throwingMerger$92(Collectors.java:133)
at java.util.stream.Collectors$$Lambda$6/1177096266.apply(Unknown Source)
at java.(HashMap.java:1245)
.....
原因是两个参数的toMap(xx, xx)⽅法, 当出现重复key触发merge时,直接抛出异常。源码如下:
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
// 注意这⾥的throwingMerger()
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
接下来我们看throwingMerger() ⽅法:【注意⽅法注释】
/**
* Returns a merge function, suitable for use in
* {@link Map#merge(Object, Object, BiFunction) ()} or
* {@link #toMap(Function, Function, BinaryOperator) toMap()}, which always
* throws {@code IllegalStateException}.  This can be used to enforce the
* assumption that the elements being collected are distinct.
*
* @param <T> the type of input arguments to the merge function
* @return a merge function which always throw {@code IllegalStateException}
*/
private static <T> BinaryOperator<T> throwingMerger() {
return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
}
...
解决⽅法
业务控制尽量不要出现重复值
出现重复 key 时,使⽤后⾯的 value 覆盖前⾯的 value
SdsTest sds1 = new SdsTest("aaa","aaa");
SdsTest sds2 = new SdsTest("bbb","bbb");
SdsTest sds3 = new SdsTest("aaa","ccc");
sdsTests.add(sds1);
sdsTests.add(sds2);
sdsTests.add(sds3);
// 写法⼀
Map<String, String> nmap = sdsTests.stream().collect(HashMap::new,(k, v) -> k.Name(), v.getAge()), HashMap::putAll); System.out.println("nmap->:" + String());
// 写法⼆
Map<String, String> nmap1 = sdsTests.stream().Map(SdsTest::getName, SdsTest::getAge, (k1, k2) -> k2)); System.out.println("nmap1->:" + String());
...
----------------------
输出:
nmap->:{aaa=ccc, bbb=bbb}
nmap1->:{aaa=ccc, bbb=bbb}
出现重复 key 时,把对应的 value 拼接起来
...
Map<String, String> nmap1 = sdsTests.stream().Map(SdsTest::getName, SdsTest::getAge, (k1, k2) -> k1 + "," + k2)); System.out.println("nmap1->:" + String());
...
----------------
输出:
nmap1->:{aaa=aaa,ccc, bbb=bbb}
把重复 key 的值拼成⼀个集合
......
Map<String, List<String>> map = sdsTests.stream().Map(SdsTest::getName,
s -> {
java streamList<String> ages = new ArrayList<>();
ages.Age());
return ages;
},
(List<String> v1, List<String> v2) -> {
v1.addAll(v2);
return v1;
}));
System.out.println("map->"+String());
------------
输出:
map->{aaa=\[aaa, ccc\], bbb=\[bbb\]}
建议:
优先业务控制,尽量避免 List 中出现重复
若存在重复场景,则根据实际业务场景选择具体⽅法【覆盖、拼接、搞成集合】
以上就是Java8 中使⽤Stream 让List 转 Map使⽤总结的详细内容,更多关于Java8 List 转 Map使⽤的资料请关注其它相关⽂章!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。