Java 8でCollectionをMapに変換する方法

9207 ワード

Java 8のjava.util.stream.Collectorsでは、CollectionをMap構造に変換するためのいくつかの方法が提供されています.この文書では、3つの個人的な理解を記録します.
Method
Return Type
groupingBy Map>
partitioningBy Map>
toMap Map
1.環境
Java: jdk1.8.0_144
2.特性説明
Student.java
public class Student {
    private String studentNo;
    private String name;
    private Boolean gender;
    private int age;

    public Student(String studentNo, String name, Boolean gender, int age) {
        this.studentNo = studentNo;
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public String getStudentNo() {
        return studentNo;
    }

    public String getName() {
        return name;
    }

    public Boolean getGender() {
        return gender;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return String.format("Student [studentNo=%s, name=%s, gender=%s, age=%s]", studentNo, name, gender, age);
    }
}

fakeStudent()メソッド
private List fakeStudent() {
    List students = new ArrayList<>();
    students.add(new Student("1", "name1", false, 2));
    students.add(new Student("2", "name2", false, 2));
    students.add(new Student("3", "name2", null, 2));
    students.add(new Student("4", "name4", true, 2));
    students.add(new Student(null, "name5", true, 2));
    return students;
}

2.1. Collectors.groupingBy
public static  Collector>> groupingBy(Function super T, ? extends K> classifier) {
    return groupingBy(classifier, toList());
}

a)Functionの戻り値で集合をグループ化し,それをKeyとし,対応するリストをValueとし,Mapを返す.
b)Key対応リストが空の場合、返されるMapにはそのKeyが含まれない
c)Functionの戻り値がNullの場合、Null PointerExceptionを投げ出す
@Test(expected = NullPointerException.class)
public void shouldThrowNPEWhenGroupingByNullKey() {
    fakeStudent().stream().collect(Collectors.groupingBy(Student::getStudentNo));
}

2.2. Collectors.partitioningBy
public static  Collector>> partitioningBy(Predicate super T> predicate) {
    return partitioningBy(predicate, toList());
}

a)Predicateの戻り値によって集合を2つのグループに分け,適合条件のリストはtrueをKey,適合しないリストはfalseをKeyとする.
b)Predicateの戻り値がNullの場合、Null PointerExceptionを投げ出す
@Test(expected = NullPointerException.class)
public void shouldReturnMapWhenPartitioningByNullKey() {
    fakeStudent().stream().collect(Collectors.partitioningBy(Student::getGender));
}

2.3. Collectors.toMap
public static  Collector> toMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper) {
    return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

a)keyMapperのFunction戻り値をKey、valueMapperのFunction戻り値をValueとしてMapを形成する
b)KeyがNullであれば、正しく戻ることができる
@Test
public void shouldReturnMapWhenToMapNullKey() {
    Map map = fakeStudent().stream()
            .collect(Collectors.toMap(Student::getStudentNo, Function.identity()));
    assertEquals("{null=Student [studentNo=null, name=name5, gender=true, age=2], "
            + "1=Student [studentNo=1, name=name1, gender=false, age=2], "
            + "2=Student [studentNo=2, name=name2, gender=false, age=2], "
            + "3=Student [studentNo=3, name=name2, gender=null, age=2], "
            + "4=Student [studentNo=4, name=name4, gender=true, age=2]}", map.toString());
}

c)Key値が重複する場合、デフォルトでIllegalStateExceptionを放出
@Test
public void shouldThrowIllegalStateExceptionWhenToMapDuplicateKey() {
    Map map = null;
    try {
        map = fakeStudent().stream().collect(Collectors.toMap(Student::getName, Function.identity()));
    } catch (Exception e) {
        assertTrue(e instanceof IllegalStateException);
        assertEquals("Duplicate key Student [studentNo=2, name=name2, gender=false, age=2]", e.getMessage());
    }
    assertNull(map);
}
Duplicate Keyの問題を回避する必要がある場合は、2つの選択肢があります.
  • toMapの競合ポリシーを決定します.たとえば、前者の
  • を指定します.
    @Test
    public void shouldReturnMapWhenToMapDuplicateKey() {
        Map map = fakeStudent().stream()
                .collect(Collectors.toMap(Student::getName, Function.identity(), (student1, student2) -> student1));
        assertEquals("{name5=Student [studentNo=null, name=name5, gender=true, age=2], "
                + "name4=Student [studentNo=4, name=name4, gender=true, age=2], "
                + "name2=Student [studentNo=2, name=name2, gender=false, age=2], "
                + "name1=Student [studentNo=1, name=name1, gender=false, age=2]}", map.toString());
    }
  • toMapメソッドを放棄しcollect
  • を利用する
    @Test
    public void shouldReturnMapWhenCollectDuplicateKey() {
        Map map = fakeStudent().stream().collect(HashMap::new, (m, v) -> m.put(v.getName(), v),
                HashMap::putAll);
        assertEquals("{name5=Student [studentNo=null, name=name5, gender=true, age=2], "
                + "name4=Student [studentNo=4, name=name4, gender=true, age=2], "
                + "name2=Student [studentNo=3, name=name2, gender=null, age=2], "
                + "name1=Student [studentNo=1, name=name1, gender=false, age=2]}", map.toString());
    }

    d)ValueがNullであればNull PointerExceptionを投げ出す
    @Test(expected = NullPointerException.class)
    public void shouldThrowNPEWhenToMapNullValue() {
        fakeStudent().stream().collect(Collectors.toMap(Student::getStudentNo, Student::getGender));
    }

    3.結語
  • Collectors.groupingBy/Collectors.partitioningByの中心思想はすべて元の集合を何らかの条件でグループ化し、グループ化条件はNullではない.ただし、Collectors.partitioningByのパケット条件は、断言であり、true/falseに対応する2つの値のセットを永遠に返します.これらの値は、Valueが空のリストである可能性がありますが、Collectors.groupingByのパケット結果が空のリストである場合、
  • が破棄されます.
    @Test
    public void shouldReturnSameMapWhenGroupingByAndPartitioningBy() {
        List students = fakeStudent().stream().filter(student -> student.getGender() != null)
                .collect(Collectors.toList());
        Map> groupingByMap = students.stream()
                .collect(Collectors.groupingBy(Student::getGender));
        Map> partitioningByMap = students.stream()
                .collect(Collectors.partitioningBy(Student::getGender));
        assertEquals("{false=[Student [studentNo=1, name=name1, gender=false, age=2], "
                + "Student [studentNo=2, name=name2, gender=false, age=2]], "
                + "true=[Student [studentNo=4, name=name4, gender=true, age=2], "
                + "Student [studentNo=null, name=name5, gender=true, age=2]]}", groupingByMap.toString());
        assertEquals(groupingByMap.toString(), partitioningByMap.toString());
    }
    
    @Test
    public void shouldReturnDifferentMapWhenGroupingByAndPartitioningBy() {
        Function function = student -> student.getAge() > 3;
        List students = fakeStudent();
        Map> groupingByMap = students.stream().collect(Collectors.groupingBy(function));
        Map> partitioningByMap = students.stream()
                .collect(Collectors.partitioningBy(function::apply));
        assertEquals("{false=[Student [studentNo=1, name=name1, gender=false, age=2], "
                + "Student [studentNo=2, name=name2, gender=false, age=2], "
                + "Student [studentNo=3, name=name2, gender=null, age=2], "
                + "Student [studentNo=4, name=name4, gender=true, age=2], "
                + "Student [studentNo=null, name=name5, gender=true, age=2]]}", groupingByMap.toString());
        assertEquals(
                "{false=[Student [studentNo=1, name=name1, gender=false, age=2], "
                        + "Student [studentNo=2, name=name2, gender=false, age=2], "
                        + "Student [studentNo=3, name=name2, gender=null, age=2], "
                        + "Student [studentNo=4, name=name4, gender=true, age=2], "
                        + "Student [studentNo=null, name=name5, gender=true, age=2]], true=[]}",
                partitioningByMap.toString());
    }
  • Collectors.toMapはCollectors.groupingBy/Collectors.partitioningByとは異なり、集合内の要素を何らかの形で1つのMapに分解するだけであり、このMapのkeyはNullであっても重複は許されないが、MapのValueはNull
  • であってはならない.
    4.参考資料
  • コードアドレスhttps://github.com/hivsuper/study/blob/master/study-java8/src/test/java/org/lxp/java8/map/CollectionToMapTest.java
  • https://stackoverflow.com/questions/27993604/whats-the-purpose-of-partitioningby

  • 転載先:https://www.cnblogs.com/hiver/p/9156147.html