Summary of the problem of using Stream to convert List to Map in Java8
- 2021-09-16 07:13:33
- OfStack
There are some problems that are not easy to find when converting List into Map by using the new feature Collectors. toMap () of Java, which are summarized here for future reference.
Null pointer risk
java.lang.NullPointerException
When null is in List, java. lang. NullPointerException is reported when Collectors. toMap () is converted to Map, as follows:
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());
---------
Running error:
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)
.....
The reason is
toMap()
Method is used in the
Map.merge()
Method merge, merge does not allow value to be caused by null, source code is as follows:
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
// Judged here value Can not be null
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
...
Solution
For service control, do not have Null value "Where there is Null, you can assign a default value". Adding judgment during conversion, if it is null, give a default value
Map<String, String> map = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, sdsTest -> sdsTest.getAge() == null ? "0" : sdsTest.getAge()));
Build with collect (...), allowing null values
Map<String, String> nmap = sdsTests.stream().collect(HashMap::new,(k, v) -> k.put(v.getName(), v.getAge()), HashMap::putAll);
// TODO Downstream business from Map Value to be done NPE Judge
Wrapping Values with 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"));
------------
Output:
bbb.age=0
Recommendations
key Repeat Risk
java.lang.IllegalStateException: Duplicate key xx
When there are duplicate values in List, when Collectors. toMap () is converted to Map, java. lang. IllegalStateException: Duplicate key xx will be reported, for example
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());
---------
Running error:
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)
.....
The reason is that the toMap (xx, xx) method with two parameters directly throws an exception when merge is triggered by repeated key. The source code is as follows:
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
// Pay attention to the throwingMerger()
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
Next, let's look at the throwingMerger () method: "Pay attention to method comments"
/**
* 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)); };
}
...
Solution
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);
// Writing style 1
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());
// Writing style 2
Map<String, String> nmap1 = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, SdsTest::getAge, (k1, k2) -> k2));
System.out.println("nmap1->:" + nmap1.toString());
...
----------------------
Output:
nmap->:{aaa=ccc, bbb=bbb}
nmap1->:{aaa=ccc, bbb=bbb}
In case of duplicate key, splice the corresponding value
...
Map<String, String> nmap1 = sdsTests.stream().collect(Collectors.toMap(SdsTest::getName, SdsTest::getAge, (k1, k2) -> k1 + "," + k2));
System.out.println("nmap1->:" + nmap1.toString());
...
----------------
Output:
nmap1->:{aaa=aaa,ccc, bbb=bbb}
Split the values of repeating key into a set
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
// Judged here value Can not be null
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
...
0
Recommendations:
The above is the detailed content of the summary of using Stream to convert List to Map in Java8. Please pay attention to other related articles on this site for more information about the use of Java8 List to Map!