Java8 中使用Stream 讓List 轉(zhuǎn) Map使用問題小結(jié)
在使用 Java 的新特性 Collectors.toMap() 將 List 轉(zhuǎn)換為 Map 時存在一些不容易發(fā)現(xiàn)的問題,這里總結(jié)一下備查。
空指針風(fēng)險
java.lang.NullPointerException
當(dāng) List 中有 null 值的時候,使用 Collectors.toMap() 轉(zhuǎn)為 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().collect(Collectors.toMap(SdsTest::getName, SdsTest::getAge));
System.out.println(map.toString());
---------
運行錯誤:
Exception in thread "main" java.lang.NullPointerException
at java.util.HashMap.merge(HashMap.java:1216)
at java.util.stream.Collectors.lambda$toMap$150(Collectors.java:1320)
.....
原因是toMap()方法中使用Map.merge()方法合并時,merge 不允許 value 為 null 導(dǎo)致的,源碼如下:
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
// 在這里判斷了value不可為null
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
...
解決方法
業(yè)務(wù)控制不要出現(xiàn) Null 值【有 Null 的地方,可以賦值默認值】在轉(zhuǎn)換時加判斷,如果為 null,則給一個默認值
Map<String, String> map = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, sdsTest -> sdsTest.getAge() == null ? "0" : sdsTest.getAge()));
使用 collect(..) 構(gòu)建,允許空值
Map<String, String> nmap = sdsTests.stream().collect(HashMap::new,(k, v) -> k.put(v.getName(), v.getAge()), HashMap::putAll); // TODO 下游業(yè)務(wù)從Map取值要做NPE判斷
使用 Optional 對值進行包裝
Map<String, Optional<String>> opmap = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, sdsTest -> Optional.ofNullable(sdsTest.getAge())));
System.out.println("bbb.age=" + opmap.get("bbb").orElse("0"));
------------
輸出:
bbb.age=0
- 優(yōu)先業(yè)務(wù)控制,盡量避免 List 中存在 Null
- 其次推薦第 4 種方法【使用 Optional 對值進行包裝】,能很好的避免 NPE 問題
key重復(fù)風(fēng)險
java.lang.IllegalStateException: Duplicate key xx
當(dāng) List 中有重復(fù)值的時候,使用 Collectors.toMap() 轉(zhuǎn)為 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().collect(Collectors.toMap(SdsTest::getName, SdsTest::getAge));
System.out.println(map.toString());
---------
運行錯誤:
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.util.HashMap.merge(HashMap.java:1245)
.....
原因是兩個參數(shù)的toMap(xx, xx)方法, 當(dāng)出現(xiàn)重復(fù)key觸發(fā)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) Map.merge()} 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)); };
}
...
- 業(yè)務(wù)控制盡量不要出現(xiàn)重復(fù)值
- 出現(xiàn)重復(fù) 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.put(v.getName(), v.getAge()), HashMap::putAll);
System.out.println("nmap->:" + nmap.toString());
// 寫法二
Map<String, String> nmap1 = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, SdsTest::getAge, (k1, k2) -> k2));
System.out.println("nmap1->:" + nmap1.toString());
...
----------------------
輸出:
nmap->:{aaa=ccc, bbb=bbb}
nmap1->:{aaa=ccc, bbb=bbb}
出現(xiàn)重復(fù) key 時,把對應(yīng)的 value 拼接起來
...
Map<String, String> nmap1 = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, SdsTest::getAge, (k1, k2) -> k1 + "," + k2));
System.out.println("nmap1->:" + nmap1.toString());
...
----------------
輸出:
nmap1->:{aaa=aaa,ccc, bbb=bbb}
把重復(fù) key 的值拼成一個集合
......
Map<String, List<String>> map = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName,
s -> {
List<String> ages = new ArrayList<>();
ages.add(s.getAge());
return ages;
},
(List<String> v1, List<String> v2) -> {
v1.addAll(v2);
return v1;
}));
System.out.println("map->"+map.toString());
------------
輸出:
map->{aaa=\[aaa, ccc\], bbb=\[bbb\]}
- 優(yōu)先業(yè)務(wù)控制,盡量避免 List 中出現(xiàn)重復(fù)
- 若存在重復(fù)場景,則根據(jù)實際業(yè)務(wù)場景選擇具體方法【覆蓋、拼接、搞成集合】
以上就是Java8 中使用Stream 讓List 轉(zhuǎn) Map使用總結(jié)的詳細內(nèi)容,更多關(guān)于Java8 List 轉(zhuǎn) Map使用的資料請關(guān)注腳本之家其它相關(guān)文章!
- 詳解Java8新特性Stream之list轉(zhuǎn)map及問題解決
- Java8 實現(xiàn)stream將對象集合list中抽取屬性集合轉(zhuǎn)化為map或list
- java8快速實現(xiàn)List轉(zhuǎn)map 、分組、過濾等操作
- java8 Stream list to Map key 重復(fù) value合并到Collectio的操作
- Java8 將一個List<T>轉(zhuǎn)為Map<String,T>的操作
- Java8中List轉(zhuǎn)Map(Collectors.toMap) 的技巧分享
- Java8中forEach語句循環(huán)一個List和Map
- Java8中List轉(zhuǎn)Map的多種方式代碼
相關(guān)文章
Java?@SpringBootApplication注解深入解析
這篇文章主要給大家介紹了關(guān)于Java?@SpringBootApplication注解的相關(guān)資料,@SpringBootApplication這個注解是Spring?Boot項目的基石,創(chuàng)建SpringBoot項目之后會默認在主類加上,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-02-02
確保SpringBoot定時任務(wù)只執(zhí)行一次的常見方法小結(jié)
在Spring Boot項目中,確保定時任務(wù)只執(zhí)行一次是一個常見的需求,這種需求可以通過多種方式來實現(xiàn),以下是一些常見的方法,它們各具特點,可以根據(jù)項目的實際需求來選擇最合適的方法,需要的朋友可以參考下2024-10-10

