Java 8中提供了几个方法可用于把Collection转为Map结构,本文记录了个人对其中三个的理解。
Method | Return Type |
---|---|
groupingBy | Map<K, List<T>> |
partitioningBy | Map<Boolean, List<T>> |
toMap | Map<K,U> |
1. 环境
Java: jdk1.8.0_144
2. 特性说明
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) {
= studentNo;
= name;
= gender;
= 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 ("Student [studentNo=%s, name=%s, gender=%s, age=%s]", studentNo, name, gender, age);
}
}
fakeStudent()方法
private List<Student> fakeStudent() {
List<Student> students = new ArrayList<>();
(new Student("1", "name1", false, 2));
(new Student("2", "name2", false, 2));
(new Student("3", "name2", null, 2));
(new Student("4", "name4", true, 2));
(new Student(null, "name5", true, 2));
return students;
}
2.1.
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
a) 按Function的返回值把集合分组,并以之为Key,对应的列表为Value,返回Map
b) 若Key对应的列表为空时,返回的Map中将不包含该Key
c) 若Function的返回值为Null,抛出NullPointerException
@Test(expected = )
public void shouldThrowNPEWhenGroupingByNullKey() {
fakeStudent().stream().collect((Student::getStudentNo));
}
2.2.
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
a) 按Predicate的返回值把集合分为两组,符合条件的列表以true为Key,不符合的列表以false为Key
b) 若Predicate的返回值为Null,抛出NullPointerException
@Test(expected = )
public void shouldReturnMapWhenPartitioningByNullKey() {
fakeStudent().stream().collect((Student::getGender));
}
2.3.
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);
}
a) 以keyMapper的Function返回值为Key且以valueMapper的Function返回值为Value,形成Map
b) 若Key为Null,依然可以正确返回
@Test
public void shouldReturnMapWhenToMapNullKey() {
Map<String, Student> map = fakeStudent().stream()
.collect((Student::getStudentNo, ()));
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]}", ());
}
c) 若Key值出现重复,默认抛出IllegalStateException
@Test
public void shouldThrowIllegalStateExceptionWhenToMapDuplicateKey() {
Map<String, Student> map = null;
try {
map = fakeStudent().stream().collect((Student::getName, ()));
} catch (Exception e) {
assertTrue(e instanceof IllegalStateException);
assertEquals("Duplicate key Student [studentNo=2, name=name2, gender=false, age=2]", ());
}
assertNull(map);
}
若需要避免Duplicate Key
的问题,可以有两个选择
- 确定toMap的冲突策略,例如指定前者
@Test
public void shouldReturnMapWhenToMapDuplicateKey() {
Map<String, Student> map = fakeStudent().stream()
.collect((Student::getName, (), (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]}", ());
}
- 放弃toMap方法,而利用collect
@Test
public void shouldReturnMapWhenCollectDuplicateKey() {
Map<String, Student> map = fakeStudent().stream().collect(HashMap::new, (m, v) -> ((), 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]}", ());
}
d) 若Value为Null,则抛出NullPointerException
@Test(expected = )
public void shouldThrowNPEWhenToMapNullValue() {
fakeStudent().stream().collect((Student::getStudentNo, Student::getGender));
}
3. 结语
- /中心思想都是把原来集合以某种条件分组,分组条件不能为Null;只是的分组条件是断言,且永远返回true/false对应的两组值,它们对应的Value可能是空列表,而的分组结果是空列表则会被抛弃
@Test
public void shouldReturnSameMapWhenGroupingByAndPartitioningBy() {
List<Student> students = fakeStudent().stream().filter(student -> () != null)
.collect(());
Map<Boolean, List<Student>> groupingByMap = ()
.collect((Student::getGender));
Map<Boolean, List<Student>> partitioningByMap = ()
.collect((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]]}", ());
assertEquals((), ());
}
@Test
public void shouldReturnDifferentMapWhenGroupingByAndPartitioningBy() {
Function<Student, Boolean> function = student -> () > 3;
List<Student> students = fakeStudent();
Map<Boolean, List<Student>> groupingByMap = ().collect((function));
Map<Boolean, List<Student>> partitioningByMap = ()
.collect((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]]}", ());
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=[]}",
());
}
- 与/不一样,它只负责把集合中的元素根据某种形式拆解为一个Map,该Map的key可以为Null但不允许重复,同时Map的Value不可以为Null
4. 参考资料
- 附代码地址
/hivsuper/study/blob/master/study-java8/src/test/java/org/lxp/java8/map/ - /questions/27993604/whats-the-purpose-of-partitioningby