文章标题:
Stream中Collectors.toMap方法的常见困惑
文章内容:
文章目录
- 一、普遍问题
- 二、键重复情况
- 2.1、报错实例
- 2.2、解决途径
- 三、值为空情况
- 3.1、报错实例
- 3.2、解决办法
- 3.1、方案其一
- 3.2、方案其二
一、普遍问题
关于stream的Collectors.toMap()方法的常见状况:
1、在将List转换为Map时,若键存在重复,就会出现报错。例如java.lang.IllegalStateException: Duplicate key这样的错误;
2、当值为null时,会引发空指针异常,即java.lang.NullPointerException。
第一种状况是由于在把List转换成Map的过程中,Map集合里的键出现了重复;
第二种状况是因为在List转Map时,Map集合里的值存在null,当使用Collectors.toMap()时就会报空指针,这是因为底层调用了Map的merge方法,而map规范里规定此处的值不能为null,所以就抛出了空指针异常。
二、键重复情况
详细内容可参考《stream流Collectors.toMap(),键值重复状况》
2.1、报错实例
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<User> list = new ArrayList<>();
User u1 = new User("张三", "男");
User u2 = new User("张三", "女");
list.add(u1);
list.add(u2);
// 键:姓名 值:性别
Map<String, String> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex));
System.out.println(userMap);
}
}
@Data
@AllArgsConstructor
class User {
// 姓名
private String name;
// 性别
private String sex;
}
报错信息:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 男
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
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:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
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 com.springboot.store.Test.main(Test.java:17)
2.2、解决途径
通过Collectors.toMap指定merge函数
能够自定义一个merge函数来确定键重复时如何获取值。比如下面这种写法,是保留最后一个值。当然也可以保留第一个,或者进行更复杂的处理。
// 键:姓名 值:性别
Map<String, String> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex, (oldValue, newValue) -> newValue));
System.out.println(userMap);
三、值为空情况
3.1、报错实例
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<User> list = new ArrayList<>();
User u1 = new User("张三", null);
list.add(u1);
// 键:姓名 值:性别
Map<String, String> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex, (o1, o2) -> o1));
System.out.println(userMap);
}
}
@Data
@AllArgsConstructor
class User {
// 姓名
private String name;
// 性别
private String sex;
}
报错信息:
Exception in thread "main" java.lang.NullPointerException
at java.util.HashMap.merge(HashMap.java:1225)
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:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
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 com.springboot.store.Test.main(Test.java:17)
3.2、解决办法
3.1、方案其一
利用Optional类来处理值,优点是还能继续使用Collectors.toMap,缺点是为null的值会被更改,这可能不符合业务上的预期。
3.2、方案其二
使用Stream#collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner)方法,能够保留为null的值。所以个人更推荐方案二。
以上两种解决方法的代码如下:
// 解决方案一,使用Optional类处理null 若值为空,设置默认值""
HashMap<String, String> map02 = list.stream()
.collect(Collectors.toMap(User::getName, s -> Optional.ofNullable(s.getSex()).orElse(""), (a, b) -> b, HashMap::new));
System.out.println(map02);
// 解决方案二,直接使用collect()方法进行规约操作 调用hashMap putAll方法, 注意键相同时,值会覆盖。
HashMap<String, String> map03 = list.stream()
.collect(HashMap::new, (map, item) -> map.put(item.getName(), item.getSex()), HashMap::putAll);
System.out.println(map03);
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
没有相关内容!
暂无评论...
