Stream把集合转成Map时,value为空报NPE异常

1. 问题描述

  1. 页面刷新时报服务器内部错误提示;
  2. 查看日志发现是使用StreamCollectors.toMap时报NullPointerException异常;

2. 背景

  1. 资源关联用户时,在设计时,没有冗余用户名称,而在页面列表中需要展示用户名称;
  2. 查看了代码实现,没有使用连表查询,而是分几步查询:
    1. 先查询出资源,过滤获取到用户id集合;
    2. 根据用户id集合查询用户信息,然后转成Map,key为id,value为name;
    3. 然后在遍历资源列表,根据id对名称进行赋值;
  3. 因测试操作,出现了用户没有用户名的情况,因此在转成Map时,报NPE错误;
  4. 和产品沟通后,针对名称为null的用户,展示用户账号,如果账号也为空,则不展示;
  5. jdk版本:1.8

3. 原因

  1. Stream流的Collectors.toMap方法,keyvalue都不能为空;
  2. Collectors类中有好几个重载的toMap方法,我们简单的看一下源码;

3.1 Collectors的toMap方法(只有key和value参数的)

  1. 在toMap的源码中,中调用了uniqKeysMapAccumulator方法对key和value进行了处理;
    在这里插入图片描述
  2. uniqKeysMapAccumulator的源码中,通过Objects.requireNonNull(valueMapper.apply(element))valueMapper.apply(element)的返回值进行了空值判断,如果为空,则抛出NEP异常;
    在这里插入图片描述

3.2 Collectors的toMap方法(带key去重校验的参数)

  1. 在toMap的源码中,调用了重载的toMap方法,
    在这里插入图片描述
  2. 在重载的toMap方法中,声明了Biconsumer函数表达式,在函数表达式中调用了map的merger方法,此时的map为HashMap类型,是由上一步调用时传递的HashMap::new
    在这里插入图片描述
  3. HashMapmerge方法中,判断了如果value为空,则抛出NPE问题
    在这里插入图片描述

4. 解决方法

  1. 增加peek处理,判断如果名称为null,则把账号赋值给名称;
  2. 增加filter处理,过滤掉name为null的值;
  3. 代码如下:
@Data
public class IdAndNameVo {

    @ApiModelProperty(value = "id")
    private Integer id;

    @ApiModelProperty(value = "名称")
    private String name;

    @ApiModelProperty(value = "名称")
    private String account;

    @ApiModelProperty(hidden = true)
    public static Map<Integer, String> toIdNameMap(List<IdAndNameVo> mouldIdNameVos) {
        if (mouldIdNameVos == null) {
            return new HashMap<>();
        }
        return mouldIdNameVos.stream()
                .peek(v -> {
                    if (v.getName() == null) {
                        v.setName(v.getAccount());
                    }
                })
                .filter(v -> v.getName() != null)
                .collect(Collectors.toMap(IdAndNameVo::getId, IdAndNameVo::getName, (v1, v2) -> v1));
    }

}

5. 小结

  1. HashMapkey不能为空,value可以为空;
  2. Collectors.toMap返回的HashMapkey不能为空,value也不能为空;
  3. 编码要谨慎,产品要求名称不能为空的,单还是出现了异常;

6. 参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值