Stream API
文章目录
一、简介
1. 概念
- 使用 Stream API 对集合、数组数据进行操作,比如查找、过滤、映射数据等操作
- 特点
- Stream 不会存储元素
- Stream 不会改变源数据,他们会返回一个持有结果的新Stream
- Stream 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行
2. 执行流程
- 通过数据源获取Stream
- 一系列的中间操作(过滤、查找等构成的中间操作链)
- 终止操作
3. 注意事项
- 执行终止操作时,才会执行中间操作
- 如果没有执行终止操作,则中间操作链都不执行
- 终止操作执行后,此Stream不可再用,如要使用,必须重新创建一个Stream实例
二、获取Stream
测试数据如下:
public class EmployeeData {
public static List<Employee> getEmployees(){
//创建一个List集合
List<Employee> list = new ArrayList<>();
list.add(new Employee(1001, "马化腾", 34, 6000.38));
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1003, "刘强东", 33, 3000.82));
list.add(new Employee(1004, "雷军", 26, 7657.37));
list.add(new Employee(1005, "李彦宏", 65, 5555.32));
list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
list.add(new Employee(1007, "任正非", 26, 4333.32));
list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
return list;
}
}
有以下四种方式获取Stream
1. 通过集合获取
- 顺序流
- 操作集合的数据时按照集合中数据的顺序进行操作
- 调用Collection接口的
default Stream<E> stream()
获取
- 并行流
- 操作集合的数据时多个线程并行的操作数据,不一定是原始集合的顺序
- 调用Collection接口的
default Stream<E> parallelStream()
获取
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//获取顺序流
Stream<Employee> stream = employees.stream();
//获取并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
2. 通过数组获取
- 调用Arrays类的
static <T> Stream<T> stream(T[] array)
获取
@Test
public void test2(){
//1. int型数组
int[] arr = new int[]{1,2,3,4,5,6};
IntStream stream = Arrays.stream(arr);
//2. 自定义数组
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Jerry");
Employee[] arr1 = new Employee[]{e1,e2};
Stream<Employee> stream1 = Arrays.stream(arr1);
}
3. 通过Stream.of()获取
- 调用Stream接口的
public static<T> Stream<T> of(T... values)
获取 - 此方法可以接收任意数量的参数
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
4. 无限流的获取
-
上述操作的数据源都是有限的,还可以创建无限个元素的流,有下述两种方式
-
迭代方式:调用Stream接口的
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
获取- seed参数表示数据源的起始数据
- UnaryOperator参数是内置函数式接口Function的子接口,其中的抽象方法指定如何迭代数据
-
生成方式:调用Stream接口的
public static<T> Stream<T> generate(Supplier<T> s)
获取- Supplier是内置的供给型接口
@Test
public void test4(){
//迭代方式
//遍历前10个偶数
//forEach是终止操作,参数是消费型接口的实例,作用是遍历流中的数据并执行相关的操作
//PrintStream ps = System.out;
//PrintStream中的println方法定义:public void println(Object x),满足方法引用格式
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//运行结果:输出了10个数据
//生成方式
//生成10个随机的Double型数据
//Math类中的random方法定义:public static double random(),满足方法引用格式
Stream.generate(Math::random).limit(10).forEach(System.out::println);
//运行结果:输出了10个数据
}
三、Stream的中间操作
1. 筛选和切片
@Test
public void test1(){
List<Employee> list = EmployeeData.getEmployees();
//1. filter(Predicate p) —— 从流中排除某些元素
Stream<Employee> stream = list.stream();
//练习:查询员工表中薪资大于7000的员工信息
//test方法的参数类型是泛型的类型
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
//2. limit(n) —— 使流中元素不超过给定数量
//必须重新生成一个Stream
list.stream().limit(3).forEach(System.out::println);
//3. skip(n) —— 跳过前n个元素,返回一个扔掉了前n个元素的流;若流中元素不足n个,则返回一个空流
list.stream().skip(3).forEach(System.out::println);
//4. distinct() —— 通过流中元素的hashCode()和equals(),去除重复元素
list.stream().distinct().forEach(System.out::println);
}
2. 映射
@Test
public void test2(){
//1. map(Function f) —— 根据函数给定的要求,将元素转换成其他形式或提取信息,然后将这些元素映射成一个流
//练习1:将集合中的字母转换成大写
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(String::toUpperCase).forEach(System.out::println);
//练习2:获取员工姓名长度大于3的员工的姓名。
List<Employee> employees = EmployeeData.getEmployees();
Stream<String> namesStream = employees.stream().map(Employee::getName);
namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
//2. flatMap(Function f) —— 根据函数给定的要求,将流中的每个值都换成另一个流,然后把所有流连接成一个流
//flatMap类似于Collection的addAll方法
//map类似于Collection的add方法
}
3. 排序
@Test
public void test3(){
//1. sorted() —— 自然排序
List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
list.stream().sorted().forEach(System.out::println);
//要求泛型的类必须实现Comparable接口
//2. sorted(Comparator com)——定制排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted( (e1,e2) -> {
//员工按照年龄排序,年龄相同的按照工资降序
int ageValue = Integer.compare(e1.getAge(),e2.getAge());
if(ageValue != 0){
return ageValue;
}else{
return -Double.compare(e1.getSalary(),e2.getSalary());
}
}).forEach(System.out::println);
}
四、Stream的终止操作
- 有终止操作时,才会执行中间操作
1. 匹配与查找
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//1. allMatch(Predicate p) —— 检查根据参数的规则是否匹配所有元素
//是否所有的员工的年龄都大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
//2. anyMatch(Predicate p) —— 检查根据参数的规则是否至少匹配一个元素
//是否存在员工的工资大于10000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
//3. noneMatch(Predicate p) —— 检查根据参数的规则是否没有匹配的元素
//是否存在员工姓“雷”
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
//4. findFirst —— 返回第一个元素
Optional<Employee> employee = employees.stream().findFirst();
//5. findAny —— 返回当前流中的任意元素
Optional<Employee> employee1 = employees.parallelStream().findAny();
//6. count —— 返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
//7. max(Comparator c) —— 返回流中最大值
//返回最高的工资
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
Optional<Double> maxSalary = salaryStream.max(Double::compare);
//8. min(Comparator c) —— 返回流中最小值
//9. forEach(Consumer c) —— 内部迭代
employees.stream().forEach(System.out::println);
}
2. 归约
@Test
public void test(){
//1. reduce(T identity, BinaryOperator<T> accumulator) —— 将流中元素反复结合起来,得到一个值
/**
* 参数1 表示计算的起始值
* 参数2 BinaryOperator extends BiFunction<T,T,T>
* BiFunction属于Function函数式接口,其中的抽象方法接收两个值,返回一个值
*/
//计算1-10的自然数的和
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//Integer中有方法public static int sum(int a, int b),符合方法引用规则
Integer sum = list.stream().reduce(0, Integer::sum);
//2. reduce(BinaryOperator<T> accumulator) —— 将流中元素反复结合起来,得到一个值
//计算公司所有员工工资的总和
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
}
3. 收集
@Test
public void test(){
//collect(Collector c) —— 将流转换为其他集合的形式,接收者是Collector接口的实现
//可以通过调用Collectors中的静态方法得到Collector的实例,并决定将流中的数据收集到哪里
//查找工资大于6000的员工,结果返回为一个List
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
}
Collectors类中的常用静态方法: