Java 8中Collection转为Map的方法
Java 8中java.util.stream.Collectors提供了几个方法可用于把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. 特性说明
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<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;
}
2.1. Collectors.groupingBy
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 = NullPointerException.class)
public void shouldThrowNPEWhenGroupingByNullKey() {
fakeStudent().stream().collect(Collectors.groupingBy(Student::getStudentNo));
}
2.2. Collectors.partitioningBy
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 = NullPointerException.class)
public void shouldReturnMapWhenPartitioningByNullKey() {
fakeStudent().stream().collect(Collectors.partitioningBy(Student::getGender));
}
2.3. Collectors.toMap
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(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<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
的问题,可以有两个选择
- 确定toMap的冲突策略,例如指定前者
@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());
}
- 放弃toMap方法,而利用collect
@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());
}
d) 若Value为Null,则抛出NullPointerException
@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对应的两组值,它们对应的Value可能是空列表,而Collectors.groupingBy的分组结果是空列表则会被抛弃
@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());
}
- Collectors.toMap与Collectors.groupingBy/Collectors.partitioningBy不一样,它只负责把集合中的元素根据某种形式拆解为一个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
Java 8中Collection转为Map的方法的更多相关文章
- 【转】java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别
原文网址:http://www.360doc.com/content/15/0427/22/1709014_466468021.shtml java 容器类使用 Collection,Map,Hash ...
- SuperDiamond在JAVA项目中的三种应用方法实践总结
SuperDiamond在JAVA项目中的三种应用方法实践总结 1.直接读取如下: @Test public static void test_simple(){ PropertiesConfigur ...
- Java编程中获取键盘输入实现方法及注意事项
Java编程中获取键盘输入实现方法及注意事项 1. 键盘输入一个数组 package com.wen201807.sort; import java.util.Scanner; public clas ...
- java中collection、map、set、list简介 (转)
Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些Collection允许相同的元 ...
- 【Java心得总结六】Java容器中——Collection
在[Java心得总结五]Java容器上——容器初探这篇博文中,我对Java容器类库从一个整体的偏向于宏观的角度初步认识了Java容器类库.而在这篇博文中,我想着重对容器类库中的Collection容器 ...
- Java开发中碰到的Map的坑
这属于我在开发中碰过的坑 ,容器中存放者对象,当clear()的时候,出现的奇葩问题.好了,直接看代码: package com.DataType.yinyong; import java.util. ...
- java Iterator Iterable Collection AbstractCollection Map关系
java.lang Interface Iterable<T> 实现该接口就可以使用for-each循环. java.util Interface Iterator<E> ...
- Java 8 中为什么要引出default方法
(原) default方法是java 8中新引入进的,它充许接口中除了有抽象方法以外,还可以拥用具有实现体的方法,这一点跟jdk8之前的版本已经完全不一样了,为什么要这样做呢? 拿List接口举例,在 ...
- java 接口中的成员变量与方法
java接口中变量的默认修饰符为 public static final int i = 3; 相当于 public static final int i = 3; java接口中方法的默认修饰符为 ...
随机推荐
- 【mac】显示隐藏文件夹
进入访达 快捷键:command+shift+.
- centos下开启htaccess
不知道原本 centOS是否默认支持 .htaccess 可能是因为我总弄配置文件无意中给搞坏了 今天要用到就查了下怎么开启 想要顺利开启需注意以下几点, 这几点都是在httpd.conf 这个配置文 ...
- csu1527: Bounty Hunter
Time Limit:5000MS Memory Limit:131072KB 64bit IO Format:%lld & %llu Submit Status id=134 ...
- ActiveMQ消息的延时和定时投递
ActiveMQ对消息延时和定时投递做了很好的支持,其内部启动Scheduled来对该功能支持,也提供了一个封装的消息类型:org.apache.activemq.ScheduledMessage,只 ...
- js 验证 输入值 全是中文
1.代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- spring 事件模式 源代码导读
一,jdk 事件对象基类 package java.util; import java.io.Serializable; public class EventObject implements Ser ...
- Flex事件机制学习-自定义事件实现类间通信 .
今天,学习Flex自定义事件,可以使两个类通信,定义一个Main类. public class Main extends Sprite { public function ...
- 重构机房收费系统你要用的——异常处理和抛出异常(try catch finally)——(vb.net)
你能保证你的程序不会出问题吗? 不能 当你的程序执行到某个地方发生了你不想要的结果.你是否想让它一错再错? 不想 你是否想让你的程序占着茅坑不拉屎? 不想 你是否想知道你的程序出错的原因? 想 个问题 ...
- VB.net版机房收费系统——结账功能实现(调错与优化)
调错部分 上一篇博客<VB.net版机房收费系统--结账功能实现(代码部分>说的是结账功能的实现,亮出了代码.是在为这篇博客做铺垫.尽管结账功能代码是借鉴的巨人的博客.可是自己比着葫芦画瓢 ...
- (转)我在北京工作这几年 – 一个软件工程师的反省
我于2007年来到北京,在北京工作这些年,先后在NEC.风行.百度几家公司担任软件工程师的职务.NEC是一家具有百年历史的传统日企,在知春路的分公司叫日电电子,我们部门主要从事机顶盒.数字电视上嵌入式 ...