Java8使用stream流map映射和groupingBy分组易错总结

886 阅读3分钟

map映射不当

  • 在使用Collectors.toMap映射实体时,如果value出现空值,会引发空指针异常。例如:
public class StreamTest {
    public static List<Student> getStudentList(){
        Student s1 = new Student("001", "李磊", 15, "新华中学");
        Student s2 = new Student("002", "韩梅梅", 14, "市第一中学");
        List<Student> studentList = new ArrayList<>();
        studentList.add(s1);
        studentList.add(s2);
        return studentList;
    }

    public static void main(String[] args) {
        //将id---name建立映射
        List<Student> studentList = getStudentList();
        try {
            studentList.get(0).setName(null);
            Map<String, String> map = studentList.stream().collect(Collectors.toMap(it -> it.getId(), it -> it.getName()));
            System.out.println(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果: java.lang.NullPointerException at java.util.HashMap.merge(HashMap.java:1225)

  • 使用Collectors.toMap还有第二个问题,当key出现重复时,会引发IllegalStateException。例如:
try {
    Student s1 = new Student("001", "李磊", 15, "新华中学2");
    studentList.add(s1);
    Map<String, String> map = studentList.stream().collect(Collectors.toMap(it -> it.getId(), it -> it.getName()));
    System.out.println(map);
} catch (Exception e) {
    e.printStackTrace();
}

控制台打印结果:java.lang.IllegalStateException: Duplicate key 李磊

此时可以利用Collectors.toMap的第三个参数进行筛选,return newValue时将根据放入list的先后顺序,取后放入的那个元素,代码如下:

Map<String, String> map = studentList.stream().collect(Collectors.toMap(it -> it.getId(), it->it.getName(),(oldVal,newVal)->newVal));
  • 推荐映射方法:
Map<String, String> map = studentList.stream().collect(HashMap::new, (m, v) -> m.put(v.getId(), v.getName()), HashMap::putAll);

使用这种方法将不会引发上面提到的value有空值或者key重复的问题。

groupingBy分组

  • 分组的key出现空值,引发空指针异常,例如:
List<Student> studentList = getStudentList();
        try {
            studentList.get(0).setId(null);
            Map<String, List<Student>> map = studentList.stream().collect(Collectors.groupingBy(Student::getId));
            System.out.println(map);
        } catch (Exception e) {
            e.printStackTrace();
        }

控制台打印结果:java.lang.NullPointerException: element cannot be mapped to a null key

改进方法,将groupingBy中的方法引用Student::getId改成表达式,如it->it.getId()==null ? "" : it.getId(),避免空值。

  • 排序问题

Collectors.groupingBy默认分组得到的map是HashMap,因此不能保证key的顺序性,但是value是有序的,如下:

List<Student> studentList = getStudentList();
        try {
            Student s3 = new Student("003", "张三", 15, "新华中学2");
            Student s4 = new Student("004", "李四", 14, "市第一中学2");
            studentList.add(0,s4);
            studentList.add(0,s3);
            //此时studentList元素顺序是:003,004,001,002
            Map<Integer, List<Student>> map = studentList.stream().collect(Collectors.groupingBy(it->it.getAge()==null ? 0 : it.getAge()));
            Set<Map.Entry<Integer, List<Student>>> entries = map.entrySet();
            for (Map.Entry<Integer, List<Student>> entry : entries) {
                System.out.println(entry.getKey()+"="+entry.getValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

控制台打印结果如下(先是结尾带2的学校,后是不带2的学校):

14=[Student{id='004', name='李四', age=14, school='市第一中学2'}, Student{id='002', name='韩梅梅', age=14, school='市第一中学'}]
15=[Student{id='003', name='张三', age=15, school='新华中学2'}, Student{id='001', name='李磊', age=15, school='新华中学'}]

如果想要保持key的有序性,可以明确使用LinkedHashMap,如下所示:

List<Student> studentList = getStudentList();
        try {
            Student s3 = new Student("003", "张三", 15, "新华中学2");
            Student s4 = new Student("004", "李四", 14, "市第一中学2");
            studentList.add(0,s4);
            studentList.add(0,s3);
            //此时studentList元素顺序是:003,004,001,002
            Map<Integer, List<Student>> map = studentList.stream().collect(Collectors.groupingBy(it->it.getAge()==null ? 0 : it.getAge(),LinkedHashMap::new,Collectors.toList()));
            Set<Map.Entry<Integer, List<Student>>> entries = map.entrySet();
            for (Map.Entry<Integer, List<Student>> entry : entries) {
                System.out.println(entry.getKey()+"="+entry.getValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

控制台打印结果如下:

15=[Student{id='003', name='张三', age=15, school='新华中学2'}, Student{id='001', name='李磊', age=15, school='新华中学'}]
14=[Student{id='004', name='李四', age=14, school='市第一中学2'}, Student{id='002', name='韩梅梅', age=14, school='市第一中学'}]

从控制台打印结果可以看到,使用LinkedHashMap::new前,没有按照元素顺序:003,004,001,002打印分组的结果,使用之后,打印结果就是元素顺序了。