前言:
第一次听说Stream流,还是在2018年刚工作的时候,我向一个同事请教某些问题,他跟我说JDK8新增了Stream流的特性,让我学习一下,当时我们公司根本没有人使用,我也没有去学习。我一次开始使用Stream流和Lambda表达式已经是2019年了,已经快过去一年了,Stream流和Lambda表达式的使用,确实方面开发和提高了效率,下面我分享一些常用的用法:
Stream流:
一、简介:
Stream流是对集合对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,例如:分组、过滤、去重等操作,它就像SQL语句一样简单。Stream流中可以使用Lambda表达式,提高编程效率和程序可读性。Stream流具有代码简洁和多核友好的优点。
二、常见方法汇总:
操作类型 | 接口方法 |
中间操作 | concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered() |
结束操作 | allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray() |
三、常见方式使用:
0.数据准备:
List<StudentVO> studentList=new ArrayList<>();
studentList.add(new StudentVO("1","小张","1",8,null,"北京顺义"));
studentList.add(new StudentVO("2","小韩","2",14,null,"北京顺义"));
studentList.add(new StudentVO("3","小王","1",20,null,"北京密云"));
studentList.add(new StudentVO("4","小周","1",13,null,"北京通州"));
studentList.add(new StudentVO("4","小李","2",7,null,"北京平谷"));
1.中间操作:
- map()用法:
//用法一:
List<String> addressList=studentList.stream().map(StudentVO::getAddress).collect(Collectors.toList());
System.out.println("map()用法一: "+addressList.toString());
//用法二:
List<Integer> ageList=studentList.stream().map(x ->x.getAge()).collect(Collectors.toList());
System.out.println("map()用法二: "+ageList.toString());
//用法三:
List<StudentDO> newStudentList=studentList.stream().map(x ->{
StudentDO studentDO = new StudentDO();
studentDO.setId(x.getId());
studentDO.setName(x.getName());
studentDO.setSex(x.getSex());
return studentDO;
}).collect(Collectors.toList());
System.out.println("map()用法三: "+newStudentList.toString());
- sorted()用法:
//自然排序:(StudentVO需要实现Comparable<E>接口,重写compareTo()方法)
List<StudentVO> studentVOAscList=studentList.stream().sorted().collect(Collectors.toList());
System.out.println("自然排序: "+studentVOAscList.toString());
//自然序逆序:
List<StudentVO> studentVODescList=studentList.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println("自然序逆序: "+studentVODescList.toString());
//自定义升序:
List<StudentVO> studentVOSortedAscList=studentList.stream().sorted(Comparator.comparing(StudentVO::getSex)).collect(Collectors.toList());
System.out.println("自定义升序: "+studentVOSortedAscList.toString());
//自定义降序:
List<StudentVO> studentVOSortedDescList=studentList.stream().sorted(Comparator.comparing(StudentVO::getSex).reversed()).collect(Collectors.toList());
System.out.println("自定义降序: "+studentVOSortedDescList.toString());
- filter()(过滤)用法:
List<StudentVO> studentVOFilterList1=studentList.stream().filter(obj ->"北京顺义".equals(obj.getAddress())).collect(Collectors.toList());
System.out.println("北京顺义的学生: "+studentVOFilterList1.toString());
List<StudentVO> studentVOFilterList2=studentList.stream().filter(obj ->obj.getAge()>=18).collect(Collectors.toList());
System.out.println("成年的学生: "+studentVOFilterList2.toString());
- distinct()(去重)用法:
List<StudentVO> studentVODistinctList=studentList.stream().distinct().collect(Collectors.toList());
- limit()(截取)用法:
List<StudentVO> studentVOLimitList=studentList.stream().limit(2).collect(Collectors.toList());
- skip()(跳过)用法:skip(Long n)
List<StudentVO> studentVOSkipList=studentList.stream().skip(2).collect(Collectors.toList());
2.结束操作:
- forEach()用法:
studentList.stream().forEach(x ->x.setAddress(x.getName()+":"+x.getPhone()));
- reduce()归约:
流由一个个元素组成,归约就是将一个个元素“折叠”成一个值,如求和、求最值、求平均值都是归约操作。
// 方式一:自定义Lambda表达式求和
int age1 = studentList.stream().map(StudentVO::getAge).reduce(0, (x1,x2)->x1+x2);
System.out.println("自定义Lambda表达式求和: "+age1);
//方式二:使用Integer.sum函数求和
int age2 = studentList.stream().map(StudentVO::getAge).reduce(0,Integer::sum);
System.out.println("使用Integer.sum函数求和: "+age2);
//注:Integer类还提供了min、max等一系列数值操作,当流中元素为数值类型时可以直接使用
//方式三:使用数值流求和
int age3 = studentList.stream().mapToInt(StudentVO::getAge).sum();
System.out.println("使用数值流求和: "+age3);
//注:
// 将普通流转换成数值流的方法:mapToInt、mapToDouble、mapToLong
// 每种数值流都提供了数值计算函数,如max、min、sum等
- collect()收集器:
收集器是对流经过筛选、映射、去重等中间操作进行后的整理,以不同的形式展现
//计数:
long count1 = studentList.stream().collect(Collectors.counting());
long count2= studentList.stream().count();
long count3= studentList.size();
System.out.println("计数:"+"count1: "+count1+" count2: "+count2+" count3: "+count3);
//最值:
//例1:年龄最大的学生
Optional<StudentVO> oldStudent =studentList.stream().collect(Collectors.maxBy(Comparator.comparingInt(StudentVO::getAge)));
//例2:年龄最小的学生
Optional<StudentVO> youngStudent =studentList.stream().collect(Collectors.minBy(Comparator.comparingInt(StudentVO::getAge)));
System.out.println("年龄最大的学生: "+oldStudent.toString()+" 年龄最小的学生: "+youngStudent.toString());
//求和:
//例:计算所有学生年龄总和
int sum = studentList.stream().collect(Collectors.summingInt(StudentVO::getAge));
System.out.println("所有学生年龄总和: "+sum);
//注:Java8提供了summingInt、summingLong、summingDouble
//平均值:
//例:计算所有学生的年龄平均值
double avg = studentList.stream().collect(Collectors.averagingInt(StudentVO::getAge));
System.out.println("所有学生的年龄平均值: "+avg);
//注:计算平均值时,不论计算对象是int、long、double,计算结果一定都是double
//一次性计算所有归约操作:
//Collectors.summarizingInt函数能一次性将最值、均值、总和、元素个数全部计算出来,并存储在对象IntSummaryStatistics中
IntSummaryStatistics collectList = studentList.stream().collect(Collectors.summarizingInt(StudentVO::getAge));
collectList.getCount();
collectList.getMin();
collectList.getMax();
collectList.getSum();
collectList.getAverage();
System.out.println("一次性计算所有归约操作: "+collectList.toString());
//连接字符串:
//例:指定分隔符,连接姓名字符串
String names = studentList.stream().map(StudentVO::getName).collect(Collectors.joining(", "));
System.out.println("指定分隔符,连接姓名字符串: "+names);
//一般性的归约操作(自定义一个归约操作):以后细研究,先学会基本使用
//自定义归约操作,需要使用Collectors.reducing函数,该函数接收三个参数:
//第一个参数为归约的初始值、第二个参数为归约操作进行的字段、第三个参数为归约操作的过程
//例:计算所有学生年龄总和
Integer sumAge = studentList.stream().collect(Collectors.reducing(0, StudentVO::getAge, (i, j) -> i + j));
System.out.println("所有学生年龄总: "+sumAge);
//#分组:
//将流中的元素按照指定类别进行划分,类似于SQL语句中的GROUPBY。
//一级分组:
//例:将所有学生分为小学、初中、高中、大学
//注:返回Map<String,List<StudentVO>>类型
Map<String,List<StudentVO>> result1 = studentList.stream()
.collect(Collectors.groupingBy((studentVO)->{
if(studentVO.getAge()>=7&&studentVO.getAge()<12){
return "小学";
} else if(studentVO.getAge()>=12&&studentVO.getAge()<15){
return "初中";
}else if(studentVO.getAge()>=15&&studentVO.getAge()<18){
return "高中";
}else if(studentVO.getAge()>=18&&studentVO.getAge()<22){
return "大学";
}
return "其他";
}));
System.out.println("一级分组: "+result1.toString());
//二级分组:
//例:将所有学生分为小学、初中、高中、大学,并且按男女分组
//注:返回Map<String, Map<String, List<StudentVO>>>类型
Map<String, Map<String, List<StudentVO>>> result2 = studentList.stream()
.collect(Collectors.groupingBy((studentVO) -> {
if (studentVO.getAge() >= 7 && studentVO.getAge() < 12) {
return "小学";
} else if (studentVO.getAge() >= 12 && studentVO.getAge() < 15) {
return "初中";
} else if (studentVO.getAge() >= 15 && studentVO.getAge() < 18) {
return "高中";
} else if (studentVO.getAge() >= 18 && studentVO.getAge() < 22) {
return "大学";
}
return "其他";
}, Collectors.groupingBy(x->{
if(x.getSex().equals("1")){
return "男";
}
return "女";
})
));
System.out.println("二级分组: "+result2.toString());
3.常用方法:
- List转Map:
Map<String, Object> map = list.stream().collect(Collectors.toMap(Student::getId, Student));
- List降序排列:
List<Student> bjProcessTaskList = list.stream().
sorted(Comparator.comparing(Student::getCreateTime).reversed()).collect(Collectors.toList());
- List转String:
String names = studentList.stream().map(StudentVO::getName).collect(Collectors.joining(", "));
- Map遍历:
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.forEach((key, value)->{
System.out.println("key:"+ key);
System.out.println("value:"+ value);
});
- 分组求和(2020-08-21):
--IntSummaryStatistics集合,包含了max,min,sum,count
Map<String, IntSummaryStatistics> collect = list.stream().collect(Collectors.groupingBy(StudentVO::getName, Collectors.summarizingInt(StudentVO::getGrade)));
--分组求和
Map<String, int> collect = list.stream().collect(Collectors.groupingBy(StudentVO::getName, Collectors.summingInt(StudentVO::getGrade)));
- 排序处理空指针(2021-05-07):
Comparator.nullsLast(Long::compareTo)
- 排序多个字段比较(2021-05-07):
Comparator.comparing(XXX::getOrderId).reversed().thenComparing(XXX::getTaskId, Comparator.nullsLast(Long::compareTo)))
Lambda表达式:
1.基本语法:
() -> expression
或
(parameters) -> expression
或
parameters -> expression
或
() ->{ statements;}
或
(parameters) ->{ statements;}
或
parameters ->{ statements;}
2.双冒号::的用法:
类名::方法名
注:此处没有()
例:
Lambda表达式: person -> person.getAge();
使用双冒号: Person::getAge
表达式:new HashMap<>()
使用双冒号:HsahMap :: new