Java 8中java.util.stream.Collectors提供了幾個方法可用於把Collection轉爲Map結構,本文記錄了我的對其中三個的理解。java
Method | Return Type |
---|---|
groupingBy | Map<K, List<T>> |
partitioningBy | Map<Boolean, List<T>> |
toMap | Map<K,U> |
Java: jdk1.8.0_144git
Student.javagithub
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()方法app
private List<Student> fakeStudent() { List<Student> 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; }
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) { return groupingBy(classifier, toList()); }
@Test(expected = NullPointerException.class) public void shouldThrowNPEWhenGroupingByNullKey() { fakeStudent().stream().collect(Collectors.groupingBy(Student::getStudentNo)); }
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) { return partitioningBy(predicate, toList()); }
@Test(expected = NullPointerException.class) public void shouldReturnMapWhenPartitioningByNullKey() { fakeStudent().stream().collect(Collectors.partitioningBy(Student::getGender)); }
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); }
@Test public void shouldReturnMapWhenToMapNullKey() { Map<String, Student> 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()); }
@Test public void shouldThrowIllegalStateExceptionWhenToMapDuplicateKey() { Map<String, Student> 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
的問題,能夠有兩個選擇ide
@Test public void shouldReturnMapWhenToMapDuplicateKey() { Map<String, Student> 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()); }
@Test public void shouldReturnMapWhenCollectDuplicateKey() { Map<String, Student> 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()); }
@Test(expected = NullPointerException.class) public void shouldThrowNPEWhenToMapNullValue() { fakeStudent().stream().collect(Collectors.toMap(Student::getStudentNo, Student::getGender)); }
@Test public void shouldReturnSameMapWhenGroupingByAndPartitioningBy() { List<Student> students = fakeStudent().stream().filter(student -> student.getGender() != null) .collect(Collectors.toList()); Map<Boolean, List<Student>> groupingByMap = students.stream() .collect(Collectors.groupingBy(Student::getGender)); Map<Boolean, List<Student>> 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<Student, Boolean> function = student -> student.getAge() > 3; List<Student> students = fakeStudent(); Map<Boolean, List<Student>> groupingByMap = students.stream().collect(Collectors.groupingBy(function)); Map<Boolean, List<Student>> 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()); }