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打印分组的结果,使用之后,打印结果就是元素顺序了。