Stream流
1. Stream流概述
概念:java8的另一个非常优秀的新特性,可以用来非常方便的对指定的集合进行各种比如 过滤 映射 排序等等复杂的操作
StreamAPI提供了对容器中数据进行高效且方便的处理方式和IO流不同,Stream流关注的是对集合中数据的一些列操作,类似于生产线,流本身并不存储数据,也不会对数据源进行改变,操作后他们会返回一个持有处理结果的一个新的stream
集合关注的是数据的存储,流关注的是数据的运算处理
- Stream关注的是对数据的运算,与CPU打交道
集合关注的是数据的存储,与内存打交道- ①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行- Stream 执行流程
① Stream的实例化
② 一系列的中间操作(过滤、映射、…)
③ 终止操作- 一个中间操作链,对数据源的数据进行处理
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
2. Stream流处理的基本步骤
Stream操作的三个步骤: 创建和获取一个流对象---->通过流对象基于数据源进行一些列的中间处理操作(形成一个处理链)—>终止操作(执行所有的中间操作并产生最终的处理结果)
Stream流的创建方式
方式一:通过集合创建Stream流
方式二:通过数组的来创建Stream流
方式三:通过Stream流的静态方法of
方式四:通过.iterate()和.generate()获取无线流
代码举例
package com.qianfeng.Test03;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamTest01 {
public static void main(String[] args) {
//方式一:通过集合创建Stream流
List<String> list1 = new ArrayList<>();
list1.add("abc");
list1.add("def");
list1.add("ghi");
//集合的第一种方式.stream()方法获取Stream流
Stream<String> stream1 = list1.stream();
stream1.forEach(System.out::print);
System.out.println("\n*************");
//集合的第二种方式.parallelStream();(此种方式是通过多线程的方式)将一个大任务不断的拆分为很多的小任务,并压给不同的线程同时执行,最后将每个子任务的结果进行合并
Stream<String> stream2 = list1.parallelStream();
stream2.forEach(System.out::print);
System.out.println("\n**************");
//方式二:通过数组的来创建Stream流
int[] arr = new int[] {1,3,5,7,9};
IntStream stream3 = Arrays.stream(arr);
stream3.forEach(System.out::print);
System.out.println("\n**************");
//方式三:通过Stream流的静态方法of
Stream<Integer> stream4 = Stream.of(1,2,3,4,5);
stream4.forEach(System.out::print);
//方式四:通过.iterate()和.generate()获取无线流
//遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
Stream流的中间操作
筛选与切片常见操作
filter():接受Lambda,从流中排出某些元素
distinct():去除重复元素(通过hashCode与equals)
limit():获取前指定个数元素
skip():跳过指定个数的元素
sorted():按照自然排序
sorted(Compartor c):定制排序
map():接受一个函数做为参数,并将该函数应用到每个集合的每个元素上将其映射为一个新元素,一般用来直接提取信息或转换为其他形式的信息
收集的常见操作
collect(Collector c):将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
代码举例
Employee类
package com.qianfeng.day22;
public class Employee {
//员工编号
private String empId;
//员工姓名
private String empName;
//员工性别
private String empGender;
//员工年龄
private int empAge;
//员工工资
private double empSalary;
//员工状态
private String empStatus;
public Employee() {
super();
}
public Employee(String empId, String empName, String empGender, int empAge, double empSalary, String empStatus) {
super();
this.empId = empId;
this.empName = empName;
this.empGender = empGender;
this.empAge = empAge;
this.empSalary = empSalary;
this.empStatus = empStatus;
}
@Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + ", empGender=" + empGender + ", empAge=" + empAge
+ ", empSalary=" + empSalary + ", empStatus=" + empStatus + "]";
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpGender() {
return empGender;
}
public void setEmpGender(String empGender) {
this.empGender = empGender;
}
public int getEmpAge() {
return empAge;
}
public void setEmpAge(int empAge) {
this.empAge = empAge;
}
public double getEmpSalary() {
return empSalary;
}
public void setEmpSalary(double empSalary) {
this.empSalary = empSalary;
}
public String getEmpStatus() {
return empStatus;
}
public void setEmpStatus(String empStatus) {
this.empStatus = empStatus;
}
}
StreamWork测试类
package com.qianfeng.day22;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class StreamWork {
public static void main(String[] args) {
List<Employee> list = new ArrayList<>();
list.add(new Employee("1001","张三","男",20,8888.8,"繁忙"));
list.add(new Employee("1002","李四","女",28,6000.128,"休闲"));
list.add(new Employee("1003","王五","男",18,7008.8,"离职"));
list.add(new Employee("1004","唐昊","男",22,10888.8,"繁忙"));
list.add(new Employee("1005","赵六","女",25,6688.8,"繁忙"));
list.add(new Employee("1006","田七","男",23,5888.8,"休闲"));
list.add(new Employee("1007","孙八","女",18,9888.8,"繁忙"));
//1.查找并输出所有工资在6000元以上的男员工
System.out.println("1.查找并输出所有工资在6000元以上的男员工:");
list.stream().filter(e->e.getEmpSalary()>=6000).filter(s->s.getEmpGender().equals("男")).forEach(System.out::println);
//2.显示年龄最小的员工信息
System.out.println("\n2.显示年龄最小的员工信息:");
list.stream().sorted((e1,e2)->-e1.getEmpAge()-e2.getEmpAge()).limit(1).forEach(System.out::println);
//3.显示最高的工资
System.out.println("\n3.显示最高的工资");
list.stream().sorted((e1,e2)->-(int)((e1.getEmpSalary()-e2.getEmpSalary())*10)).limit(1).forEach(e->System.out.println(e.getEmpSalary()));
//4.统计所有员工工资的平均值
System.out.println("\n4.统计所有员工工资的平均值");
System.out.println(list.stream().collect(Collectors.averagingDouble(Employee::getEmpSalary)).doubleValue());
// Double d = list.stream().collect(Collectors.averagingDouble(Employee::getEmpSalary));
// System.out.println(d);
//5.显示目前处于休闲状态的员工人数
System.out.println("\n5.显示目前处于休闲状态的员工人数");
System.out.println(list.stream().filter(e->e.getEmpStatus().equals("休闲")).map(e->e).collect(Collectors.toSet()).size());
//6.查找所有离职员工的员工姓名并显示
System.out.println("\n6.查找所有离职员工的员工姓名并显示");
list.stream().filter(e->e.getEmpStatus().equals("离职")).forEach(e->System.out.println(e.getEmpName()));
//7.查找所有姓王的员工并收集到一个set集合中
System.out.println("\n7.查找所有姓王的员工并收集到一个set集合中");
Set<Employee> set = list.stream().filter(e->e.getEmpName().startsWith("王")).map(e->e).collect(Collectors.toSet());
System.out.println(set);
//8.按照员工的状态信息对所有员工进行分组操作,并得到一个Map集合,遍历这个结果输出每种状态的各自人数
System.out.println("\n8.按照员工的状态信息对所有员工进行分组操作,并得到一个Map集合,遍历这个结果输出每种状态的各自人数");
Map<String, List<Employee>> map = list.stream().collect(Collectors.groupingBy(Employee::getEmpStatus));
System.out.println("繁忙状态人数" + map.get("繁忙").size());
System.out.println("休闲状态人数" + map.get("休闲").size());
System.out.println("离职状态人数" + map.get("离职").size());
}
}
终止操作:不再返回一个Stream流对象
allMatch:检查集合中元素是否都匹配
boolean b1 = list.stream().allMatch(s->s.getAge()>=18);//检查是否都成年了
anyMatch:检查是否至少有一个匹配元素
boolean b2 = list.stream().anyMatch(s->s.getScore()==100);
findFirst()返回第一个元素
//找到成绩最高的学生
Optional<Student> s = list.stream().sorted((s1,s2)->-Double.compare(s1.getScore(), s2.getScore())).limit(1)
.findFirst();//将可能为空的结果封装到Optional类中
Student student = s.get();//获取对象时如果不存在则抛出No value present异常,比传统的空指向异常更容易定位
System.out.println(student.getName());
Optional也是一个容器类,代表里面的值存在或不存在,传统的程序中是用null来表示一个对象不存在,主要作用是避免和减少程序中的空指向异常
max():获取最大值
min():获取最小值
count():计数
//查找分数最高的学生
Optional<Student> max = list.stream().max((s1,s2)->Double.compare(s1.getScore(), s2.getScore()));
try {
Student s= max.get();
System.out.println(s);
}catch (Exception e) {
// TODO: handle exception
}
//查找最低的分数
Optional<Double> min = list.stream().map(Student::getScore).min(Double::compare);
System.out.println(min.get());
//统计及格的人数
long count = list.stream().filter(s->s.getScore()>=60).count();//一定有结果,无需封装成optional
System.out.println(count);
需求:想要计算总分,没有直接提供sum
reduce:将流中的元素反复结合起来进行指定的运算
Optional<Double> total1 = list.stream().map(Student::getScore).reduce((x,y)->x+y);
Optional<Double> total2 = list.stream().map(Student::getScore).reduce(Double::sum);
System.out.println(total1.get());
System.out.println(total2.get());
collect()接收一个Collector接口收集器参数并将流处理的结果收集到指定的容器中
Collector接口的实现决定了将处理结果收集到List Set还是其他的一些容器中,我们一般不会直接创建接口对象,而是借助Collectors工具类提供的静态方法来获得一个收集器实例
//提取所有的学生姓名并收集到一个List中
List<String> result = list.stream().map(Student::getName).collect(Collectors.toList());
System.out.println(result);
//提取所有的学生姓名并收集到一个Set中
Set<String> result2 = list.stream().map(Student::getName).collect(Collectors.toSet());
System.out.println(result2);//排重
//如果想要收集结果到一些特殊的容器中比如LinkedHashSet
LinkedHashSet<String> result3 = list.stream().map(Student::getName).collect(Collectors.toCollection(LinkedHashSet::new));
System.out.println(result3);
Collectors还提供了对于单个统计结果的常用收集器
//统计及格人数
Long count = list.stream().filter(s->s.getScore()>=60).collect(Collectors.counting());
System.out.println(count);
//统计平均分
Double avg = list.stream().collect(Collectors.averagingDouble(Student::getScore));
System.out.println(avg);
//获取年龄最大的学生
Optional<Student> op = list.stream().collect(Collectors.maxBy((s1,s2)->Integer.compare(s1.getAge(), s2.getAge())));
System.out.println(op.get());
Collectors工具类还支持集合的分组操作:
//按照指定的属性进行分组并得到一个map集合
Map<String, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getGender));
System.out.println(map.size());
//按照成绩将所有学生分为及格和不及格的两个区
Map<Boolean, List<Student>> map2 = list.stream().collect(Collectors.partitioningBy(s->s.getScore()>=60));
System.out.println(map2);
//分组后再分别汇总
//统计及格和不及格分别的人数
Map<Boolean, Long> result = list.stream().collect(Collectors.partitioningBy(s->s.getScore()>=60,Collectors.counting()));
System.out.println(result);
//等性别分组后去两个性别中工资最高的
Map<String, Employee> collect = list.stream().collect(Collectors.groupingBy(
(param -> param.getEmpGender()), HashMap::new, Collectors.collectingAndThen(Collectors.maxBy((p1, p2) -> Double.compare(p1.getEmpSalary(), p2.getEmpSalary())), Optional::get)));
System.out.println(collect);
//对性别和状态两种进行分组,然后取这几种中工资最高的
Map<BaseEmployee, Employee> collect1 = list.stream().collect(Collectors.groupingBy(
(param -> BaseEmployee.builder().empGender(param.getEmpGender()).empStatus(param.getEmpStatus()).build()),
HashMap::new,
Collectors.collectingAndThen(Collectors.maxBy((p1, p2) -> Double.compare(p1.getEmpSalary(), p2.getEmpSalary())), Optional::get)));
System.out.println(collect1);