Java 8中Collectors.toMap空指针异常源码解析

 更新时间:2023年08月10日 11:55:38   作者:十方  
这篇文章主要为大家介绍了Java 8中Collectors.toMap空指针异常源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

当需要将一个List转换为Map时,可以使用 Java 8 中的 Collectors.toMap() 方法,Map是由key-value组成的键值对集合,在使用Collectors.toMap() 方法时,如果值为空,会报空指针异常,下面通过一个实例来验证一下。

Student.java 类

首先定义一个 Student.java 类

package com.magic.npe;
public class Student {
    private String name;
    private Integer age;
    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

Test.java 类创建

再创建一个 Test.java 类,用来验证将 List<Student> 转换为 Map<String, Integer>。

package com.magic.npe;
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<Student> students = new ArrayList<>();
        students.add(new Student("张三", 18));
        students.add(new Student("李四", 21));
        students.add(new Student("王五", null));
        Map<String, Integer> studentMap = students.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
        System.out.println(studentMap);
    }
}

运行程序,直接报出如下的错误信息

Exception in thread "main" java.lang.NullPointerException
    at java.util.HashMap.merge(HashMap.java:1224)
    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:1374)
    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.magic.npe.Test.main(Test.java:16)

查看Collectors.toMap()

查看一下 Collectors.toMap() 方法的源码,如下:

public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

默认会创建一个 HashMap,继续查看源码:

public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                             Function<? super T, ? extends U> valueMapper,
                             BinaryOperator<U> mergeFunction,
                             Supplier<M> mapSupplier) {
    BiConsumer<M, T> accumulator
        = (map, element) -> map.merge(keyMapper.apply(element),
                                      valueMapper.apply(element), mergeFunction);
    return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}

通过 Map.merge() 方法来合并,源码如下:

default V merge(K key, V value,
                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    Objects.requireNonNull(value);
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
    remappingFunction.apply(oldValue, value);
    if(newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

可以看到,在 merge() 方法中,要求 value 值不能为空

Objects.requireNonNull(value);

继续查看一下 Objects.requireNonNull() 方法的源码

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

NullPointerException 异常

如果值为空,则会直接报出 NullPointerException 异常。

那么,对于这个空指针异常问题,如何解决呢?一般有两种方式:

(1)替换空值null为一个默认值,比如0

package com.magic.npe;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class Test {
  public static void main(String[] args) {
      List<Student> students = new ArrayList<>();
      students.add(new Student("张三", 18));
      students.add(new Student("李四", 21));
      students.add(new Student("王五", null));
      // 如果age为null,则直接赋值默认值0
      Map<String, Integer> studentMap = students.stream().collect(Collectors.toMap(Student::getName, s -> Optional.ofNullable(s.getAge()).orElse(0)));
      System.out.println(studentMap);
  }
}

运行程序,可以看到如下输出

{李四=21, 张三=18, 王五=0}

(2)调用 collect() 的其他实现方法

package com.magic.npe;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test {
  public static void main(String[] args) {
      List<Student> students = new ArrayList<>();
      students.add(new Student("张三", 18));
      students.add(new Student("李四", 21));
      students.add(new Student("王五", null));
      Map<String, Integer> studentMap = students.stream().collect(HashMap::new, (map, student) -> map.put(student.getName(), student.getAge()), HashMap::putAll);
      System.out.println(studentMap);
  }
}

运行程序,可以看到如下输出

{李四=21, 张三=18, 王五=null}

以上就是Java 8中Collectors.toMap空指针异常源码解析的详细内容,更多关于Java8 Collectors.toMap空指针异常的资料请关注脚本之家其它相关文章!

相关文章

  • Java 线程池框架

    Java 线程池框架

    本文主要介绍了Java 线程池框架的相关知识。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • java实现日期拆分的方法

    java实现日期拆分的方法

    这篇文章主要介绍了java实现日期拆分的方法,基于java日期类实现对日期字符串的拆分功能,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • java冒泡排序简单实例

    java冒泡排序简单实例

    本文主要介绍了JSONjava冒泡排序实例与思路分析。具有一定的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • Spring通过<import>标签导入外部配置文件

    Spring通过<import>标签导入外部配置文件

    之前文章里我们讲到Spring加载Xml配置文件的细节,那么加载完了我们肯定要解析这个配置文件中定义的元素。这篇我们首先来分析下Spring是如何通过标签导入外部配置文件的。
    2021-06-06
  • 通过代码示例了解submit与execute的区别

    通过代码示例了解submit与execute的区别

    这篇文章主要介绍了通过代码示例了解submit与execute的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • 浅析SpringBoot统一返回结果的实现

    浅析SpringBoot统一返回结果的实现

    前后端开发过程中数据交互规范化是一件非常重要的事情,不仅可以减少前后端交互过程中出现的问题,也让代码逻辑更加具有条理,下面小编就和大家讲讲SpringBoot如何统一返回结果的吧
    2023-07-07
  • Java中Stream流对多个字段进行排序的方法

    Java中Stream流对多个字段进行排序的方法

    我们在处理数据的时候经常会需要进行排序后再返回给前端调用,比如按照时间升序排序,前端展示数据就是按时间先后进行排序,下面这篇文章主要给大家介绍了关于Java中Stream流对多个字段进行排序的相关资料,需要的朋友可以参考下
    2023-10-10
  • Java基于阻塞队列实现生产者消费者模型示例详解

    Java基于阻塞队列实现生产者消费者模型示例详解

    这篇文章主要介绍了Java基于阻塞队列实现生产者消费者模型,阻塞队列的特点就是阻塞两个字,阻塞功能使得生产者和消费者两端的能力得以平衡,当有任何一端速度过快时,阻塞队列便会把过快的速度降下来,感兴趣的朋友可以参考下
    2023-12-12
  • 关于Spring Bean实例过程中使用反射和递归处理的Bean属性填充问题

    关于Spring Bean实例过程中使用反射和递归处理的Bean属性填充问题

    本文带领大家一起学习下在Spring Bean实例过程中如何使用反射和递归处理的Bean属性填充,需要在类 AbstractAutowireCapableBeanFactory 的 createBean 方法中添加补全属性方法,具体操作方法跟随小编一起学习下吧
    2021-06-06
  • Java日期时间以及日期相互转换

    Java日期时间以及日期相互转换

    这篇文章主要为大家详细介绍了Java日期时间,以及日期相互转换的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10

最新评论