前言
在日常的编码中,我们经常要对集合类对象进行处理。JDK8之前,我们可能要用for循环等方式来处理集合中的每一个元素。不过JDK8的lambda表达式给了我们更方便的选择。
关于lambda表达式和stream的介绍,这里有一篇文章写得非常好,详细易懂。JDK 8 函数式编程入门
直接上demo
既然本文是要写一些常用的例子,那就直接上demo了。
1.先创建一个简单的实体类
@Data
@Builder
public class DefaultTestBean {
private int id;
private String name;
private String area;
private double score;
}
2.初始化一个我们要处理的list
private List<DefaultTestBean> initList() {
List<DefaultTestBean> resultList = Lists.newArrayList();
resultList.add(DefaultTestBean.builder().id(1).name("小白").area("A").score(10.1).build());
resultList.add(DefaultTestBean.builder().id(2).name("小红").area("B").score(9.6).build());
resultList.add(DefaultTestBean.builder().id(3).name("小黄").area("A").score(5.9).build());
resultList.add(DefaultTestBean.builder().id(4).name("小蓝").area("C").score(3.3).build());
return resultList;
}
3.开始搞事情,看注释就好
/**
* 盘他!
*/
public void showMeHowToUseStream() {
List<DefaultTestBean> list = initList();
PrintUtil.printJSONString("initList", list);
// 1.【转list】提取list中对象的某个字段,返回list。用map进行类型转换,用collect(Collectors.toList())返回新的list。
List<String> nameList = list.stream().map(DefaultTestBean::getName).collect(Collectors.toList());
PrintUtil.printJSONString("nameList", nameList);
// 2.【过滤】对某个字段过滤,用filter。只有符合filter中的bean才会保留下来。
List<DefaultTestBean> highCoreList = list.stream().filter(bean -> bean.getScore() > 5.0).collect(Collectors.toList());
PrintUtil.printJSONString("highCoreList", highCoreList);
// 3.【排序】根据id这个字段返回倒序排序list。如果是a-b,则是升序。如果是b-a,则是倒序。
List<DefaultTestBean> sortList = list.stream().sorted((a, b) -> b.getId() - a.getId()).collect(Collectors.toList());
PrintUtil.printJSONString("sortList", sortList);
// 4.【转map】list转成map,key为id,value为bean。用Collectors.toMap转成map。
Map<Integer, DefaultTestBean> idBeanMap = list.stream().collect(Collectors.toMap(DefaultTestBean::getId, bean -> bean));
PrintUtil.printJSONString("idBeanMap", idBeanMap);
// 5.【转map】提取list中信息转成map,key为id,value为name。同上。
Map<Integer, String> idNameMap = list.stream().collect(Collectors.toMap(DefaultTestBean::getId, DefaultTestBean::getName));
PrintUtil.printJSONString("idNameMap", idNameMap);
// 6.【转map分组】对list中的bean进行分组,key为area,value为list。
Map<String, List<DefaultTestBean>> areaListMap = list.stream().collect(Collectors.groupingBy(DefaultTestBean::getArea));
PrintUtil.printJSONString("areaListMap", areaListMap);
}
public static <T> void printJSONString(String title, T t) {
System.out.println(title + ":" + JSON.toJSONString(t, SerializerFeature.IgnoreNonFieldGetter));
}
执行showMeHowToUseStream方法,我们可以看到以下输出:
initList:[{"area":"A","id":1,"name":"小白","score":10.1},{"area":"B","id":2,"name":"小红","score":9.6},{"area":"A","id":3,"name":"小黄","score":5.9},{"area":"C","id":4,"name":"小蓝","score":3.3}]
nameList:["小白","小红","小黄","小蓝"]
highCoreList:[{"area":"A","id":1,"name":"小白","score":10.1},{"area":"B","id":2,"name":"小红","score":9.6},{"area":"A","id":3,"name":"小黄","score":5.9}]
sortList:[{"area":"C","id":4,"name":"小蓝","score":3.3},{"area":"A","id":3,"name":"小黄","score":5.9},{"area":"B","id":2,"name":"小红","score":9.6},{"area":"A","id":1,"name":"小白","score":10.1}]
idBeanMap:{1:{"area":"A","id":1,"name":"小白","score":10.1},2:{"area":"B","id":2,"name":"小红","score":9.6},3:{"area":"A","id":3,"name":"小黄","score":5.9},4:{"area":"C","id":4,"name":"小蓝","score":3.3}}
idNameMap:{1:"小白",2:"小红",3:"小黄",4:"小蓝"}
areaListMap:{"A":[{"area":"A","id":1,"name":"小白","score":10.1},{"area":"A","id":3,"name":"小黄","score":5.9}],"B":[{"area":"B","id":2,"name":"小红","score":9.6}],"C":[{"area":"C","id":4,"name":"小蓝","score":3.3}]}
4.有些小坑
在转map时,key值需要唯一,否则会报错。
看demo:
public void trap() {
List<DefaultTestBean> list = initList();
// 1.转map时,key值需要唯一。否则会报错。
list.add(DefaultTestBean.builder().id(1).name("白小白").area("A").score(10.1).build());
// 有重复,报错:java.lang.IllegalStateException: Duplicate key 小白
Map<Integer, String> idNameMap = list.stream().collect(Collectors.toMap(DefaultTestBean::getId, DefaultTestBean::getName));
PrintUtil.printJSONString("idNameMap", idNameMap);
// 我们可以用Collectors.toMap的重载方法,传入一个合并函数来决定如何合并相同key的值即可。
// 我们默认返回list中第一个出现的重复的值
Map<Integer, String> idNameMap = list.stream().collect(Collectors.toMap(DefaultTestBean::getId, DefaultTestBean::getName, (value1, value2) -> value1));
PrintUtil.printJSONString("idNameMap", idNameMap);
// 我们把两个值按照自己的逻辑合并到一起
Map<Integer, String> idMergeNameMap = list.stream().collect(Collectors.toMap(DefaultTestBean::getId, DefaultTestBean::getName, (value1, value2) -> value1 + "和" + value2));
PrintUtil.printJSONString("idMergeNameMap", idMergeNameMap);
}
}
总结
个人认为,对于集合类的日常处理,差不多就是这几种。如果以后发现还会经常用到其他的处理方式,会补充进来的。
其次,自己在想这种把内容直接写到代码注释里的方式,可读性如何。因为我感觉对于一些实用性的文章,还是要多上一些代码和demo会有更高的价值。所以想着,如果直接把代码丢上来,然后把文章要解释的内容以注释的形式填充到代码里,那是不是看完这段代码就能一次性的接受了内容和实践效果?之后的文章也可以继续这样试一试。