Stream 告诉你什么叫优雅
0 概述
- 了解 Stream 之前,要先知道 Lambda 表达式,在 Lambda 表达式之前,要使用函数作为参数的话,需要写大量的匿名内部类,这通常会使代码变得冗余和难以维护,引入 Lambda 表达式后,它允许我们以匿名函数的形式来表达行为参数化,从而使代码更加简洁和可读。归根结底,可以把 Lambda 表达式看作是一种语法糖。
- Stream 是 Java 8 中一个重要的新特性,它允许我们以声明性方式处理数据。你可以将 Stream 看作是一个来自数据源的元素的序列,支持顺序和并行聚合操作。与传统的 for-loop 不同,使用 Stream 可以使我们的代码更加简洁,并可以方便地并行处理数据。
- Stream 不会存储元素,它只是按需计算数据。这意味着 Stream 操作是惰性的,只有在终端操作时才会真正计算。
- 使用前:这玩意儿有什么用?
- 使用后:真香 ~ 从此代码里全是 Lambda + Stream
1 优缺点
- Lambda 表达式的优点:
- 减少了大量冗余代码
- 提供了并行流,可以很容易地利用多核处理器
- Lambda 表达式的缺点:
- 可读性差,不够直观,后期维护比较难受
- 当需要 Debug 时,调试比较困难,难以定位错误信息
- Stream 流的优点:
- 使代码更加清晰和简洁,用更少的代码来实现相同的功能
- 支持函数式编程
- API 丰富,如过滤、映射、排序、归约等,可以方便地进行各种数据转换和处理操作
- Stream 流的缺点:
- 流操作是基于内存的,对于处理大量数据的情况,可能会占用较多的内存,并且可能导致性能问题
- 某些复杂的迭代逻辑可能更适合使用传统的 for-loop
2 使用演示
-
说明:
- Stream 自己不会存储元素
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream
- Stream 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行
-
使用流程:
Stream 实例化
==>中间操作(过滤、映射 ...)
==>终止操作
-
创建 Stream:
/** * 创建 Stream * @Author Jasper * @Time 2024/02/08 * @公众号:EzCoding */ public class Stream01 { @Test public void test01(){ // 创建 Stream // 方式1: 通过 Collection 系列集合提供的 stream() 或 parallelStream() List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); // 方式二: 通过 Arrays 工具类的静态方法 stream() 获取数组流 Employee[] employees = new Employee[10]; Stream<Employee> employeeStream = Arrays.stream(employees); // 方式三: 通过 Stream 类中的静态方法 of() Stream<String> stringStream = Stream.of("aa", "bb", "cc"); // 方式四: 创建无限流 // 迭代 Stream<Integer> integerStream = Stream.iterate(0, (x) -> x + 2); integerStream.limit(5).forEach(System.out::println); // 生成 Stream.generate(() -> Math.random()).limit(10).forEach(System.out::println); } }
-
Stream 的中间操作(常用的方法):
方法 描述 filter(Predicate p) 接收 Lambda,从流中过滤某些元素 distinct() 去重,默认根据 hashCode() 和 equals() 方法去重 limit(long max) 截断,限制流中元素的个数 skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流,若不足 n 个,返回空的流,常与 limit() 进行联合使用 map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素,并将其映射为一个新的元素 mapToInt(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流(扁平化) sorted() 产生一个新的流,按自然顺序排序 sorted(Comparator com) 产生一个新的流,按比较器顺序排序 /** * 中间操作 * @Author Jasper * @Time 2024/02/08 * @公众号:EzCoding */ public class Stream01 { @Test public void test02(){ // 中间操作:不会执行任何操作 List<Employee> employees = Arrays.asList( new Employee("张三",20), new Employee("李四",18), new Employee("王五",23), new Employee("赵六",23), new Employee("田七",25) ); Stream<Employee> employeeStream = employees.stream().filter(e -> { System.out.println("中间操作"); return e.getAge() > 20; }); // 终止操作:一次性执行全部内容 // 内部迭代:迭代操作由 Stream API 完成 employeeStream.forEach(System.out::println); } /* 输出结果: 中间操作 中间操作 中间操作 Employee{name='王五', age=23} 中间操作 Employee{name='赵六', age=23} 中间操作 Employee{name='田七', age=25} */ ---------------------------------------------------------------------------------------------------------- @Test public void test03(){ // 中间操作:不会执行任何操作 List<Employee> employees = Arrays.asList( new Employee("王五",23), new Employee("赵六",23), new Employee("张三",20), new Employee("李四",18), new Employee("田七",25) ); employees.stream() .filter(employee -> { System.out.println("短路"); return employee.getAge() > 20; }) .limit(2) .forEach(System.out::println); } /* 输出结果: 短路 Employee{name='王五', age=23} 短路 Employee{name='赵六', age=23} */ ---------------------------------------------------------------------------------------------------------- @Test public void test04(){ // 中间操作:不会执行任何操作 List<Employee> employees = Arrays.asList( new Employee("张三",20), new Employee("李四",18), new Employee("王五",23), new Employee("赵六",23), new Employee("田七",25) ); employees.stream() .filter( e -> e.getAge() <= 20) .skip(1) // 跳过 .distinct() // 去重:【注意】是通过hashcode和equals去重,所以必须重写这两个方法 .forEach(System.out::println); } /* 输出结果: Employee{name='李四', age=18} */ }
/** * 中间操作 * @Author Jasper * @Time 2024/02/08 * @公众号:EzCoding */ public class Stream02 { @Test public void test01(){ List<String> list = Arrays.asList("aa","bb","cc","dd","ee"); list.stream() .map((str) -> str.toUpperCase()) .forEach(System.out::println); } -------------------------------------------------------------------------------------------------------------------- @Test public void test02(){ List<String> list = Arrays.asList("aa","bb","cc","dd","ee"); Stream<Stream<Character>> stream = list.stream().map(Stream02::filterCharacter); stream.forEach( sm -> { // 先遍历map中的每一个流 sm.forEach(System.out::println); // 再从每一个流中遍历字符 }); System.out.println("-----------"); list.stream() // 【注意】使用flatMap,效果和上面的一样,类比add()和addAll() .flatMap(Stream02::filterCharacter) .forEach(System.out::println); } public static Stream<Character> filterCharacter(String str){ List<Character> arrayList = new ArrayList<>(); for(Character ch : str.toCharArray()){ arrayList.add(ch); } return arrayList.stream(); } }
/** * 中间操作 * @Author Jasper * @Time 2024/02/08 * @公众号:EzCoding */ public class Stream03 { List<Employee> employees = Arrays.asList( new Employee("张三",20), new Employee("李四",18), new Employee("王五",23), new Employee("赵六",23), new Employee("田七",25) ); @Test public void test01(){ // 自然排序(Comparable) List<String> list = Arrays.asList("jasper","g","bc","kk","tom"); Stream<String> sorted = list.stream().sorted(); sorted.forEach(System.out::println); System.out.println("----------------"); // 定制排序(Comparator) employees.stream().sorted( (e1,e2) -> { if(e1.getAge().equals(e2.getAge())){ return e1.getName().compareTo(e2.getName()); }else{ return -e1.getAge().compareTo(e2.getAge()); } }).forEach(System.out::println); } }
-
Stream 的终止操作:
方法 描述 allMatch(Predicate p) 检查是否匹配所有元素 anyMatch(Predicate p) 检查是否至少匹配一个元素 noneMatch(Predicate p) 检查是否没有匹配所有元素 findFirst() 返回第一个元素 findAny() 返回当前流中的任意元素 count() 返回流中元素的总数 max(Comparator com) 返回流中最大值 min(Comparator com) 返回流中最小值 foreach(Comsumer c) 内部迭代 reduce(BinaryOperator b) 将流中元素反复结合,得到一个值,返回 Optional< T > collect(Collector c) 将流转为其他数据结构做汇总:toSet()、toList()、counting() /** * 终止操作 * @Author Jasper * @Time 2024/02/08 * @公众号:EzCoding */ public class Stream04 { List<Employee> employees = Arrays.asList( new Employee("张三",20, Employee.Status.FREE), new Employee("李四",18, Employee.Status.BUSY), new Employee("王五",23, Employee.Status.VOCATION), new Employee("赵六",23, Employee.Status.FREE), new Employee("田七",25, Employee.Status.BUSY) ); @Test public void test(){ // 检查是否匹配所有元素 boolean allMatch = employees.stream().allMatch(e -> Employee.Status.BUSY.equals(e.getStatus())); System.out.println(allMatch); // 输出:false ------------------------------------------------------------------------------------------------------ // 检查是否至少匹配一个元素 boolean anyMatch = employees.stream().anyMatch(e -> Employee.Status.FREE.equals(e.getStatus())); System.out.println(anyMatch); // 输出:true ------------------------------------------------------------------------------------------------------ // 检查是否没有匹配的元素 boolean noneMatch = employees.stream().noneMatch(e -> Employee.Status.VOCATION.equals(e.getStatus())); System.out.println(noneMatch); // 输出:false ------------------------------------------------------------------------------------------------------ // 返回第一个元素 Optional<Employee> employee = employees.stream().sorted(Comparator.comparingInt(Employee::getAge)).findFirst(); System.out.println(employee.get()); // 输出: Employee{name='李四', age=18, status=BUSY} ------------------------------------------------------------------------------------------------------ // 返回当前流中的任意元素 Optional<Employee> anyFreeEmployee = employees.stream() .filter(e -> e.getStatus().equals(Employee.Status.FREE)) .findAny(); System.out.println(anyFreeEmployee.get()); ------------------------------------------------------------------------------------------------------ // 返回流中元素的总个数 System.out.println(employees.stream().count()); // 输出: 5 ------------------------------------------------------------------------------------------------------ // 返回流中的最大值: 比如获取年龄最大的员工(对象) Optional<Employee> max = employees.stream().max(Comparator.comparingInt(Employee::getAge)); System.out.println(max.get()); // 输出: Employee{name='田七', age=25, status=BUSY} ------------------------------------------------------------------------------------------------------ // 返回流中的最小值: 比如获取员工中最小的年龄(【注意】不是最小年龄的员工对象) // 先通过map映射筛选出员工的年龄,再根据年龄进行比较 Optional<Integer> min = employees.stream().map(Employee::getAge).min(Integer::compare); System.out.println(min.get()); // 输出: 18 } }
/** * 终止操作 * @Author Jasper * @Time 2024/02/08 * @公众号:EzCoding */ // 收集 public class Stream06 { List<Employee> employees = Arrays.asList( new Employee("张三",20, Employee.Status.FREE), new Employee("李四",18, Employee.Status.BUSY), new Employee("王五",23, Employee.Status.VOCATION), new Employee("赵六",23, Employee.Status.FREE), new Employee("田七",25, Employee.Status.BUSY) ); @Test public void test01(){ // 收集员工的姓名到一个list中 List<String> list = employees.stream() .map(Employee::getName) .collect(Collectors.toList()); // .collect(Collectors.toSet()); // 收集员工的姓名到一个set中 list.forEach(System.out::println); System.out.println("-----------------"); // 收集员工的姓名到一个自定义的集合中 HashSet<String> hashSet = employees.stream() .map(Employee::getName) .collect(Collectors.toCollection(HashSet::new)); hashSet.forEach(System.out::println); } @Test public void test02(){ // 收集总数 Long collect = employees.stream().collect(Collectors.counting()); System.out.println(collect); // 5 ---------------------------------------------------------------------------------------------------------- // 平均值 Double collect1 = employees.stream().collect(Collectors.averagingInt(Employee::getAge)); System.out.println(collect1); // 22.8 ---------------------------------------------------------------------------------------------------------- // 总和 Integer collect2 = employees.stream().collect(Collectors.summingInt(Employee::getAge)); System.out.println(collect2); // 109 ---------------------------------------------------------------------------------------------------------- // 最大值(员工对象) Optional<Employee> collect3 = employees.stream().collect(Collectors.maxBy(Comparator.comparingInt(Employee::getAge))); System.out.println(collect3.get()); // Employee{name='田七', age=25, status=BUSY} ---------------------------------------------------------------------------------------------------------- // 最小值(年龄) Optional<Integer> collect4 = employees.stream().map(Employee::getAge).collect(Collectors.minBy(Integer::compare)); System.out.println(collect4.get()); // 18 ---------------------------------------------------------------------------------------------------------- // 分组(按状态) Map<Employee.Status, List<Employee>> collect5 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus)); collect5.forEach( (x,y) -> { System.out.println(x + "=" + collect5.get(x)); }); /* 输出: BUSY=[Employee{name='李四', age=18, status=BUSY}, Employee{name='田七', age=25, status=BUSY}] VOCATION=[Employee{name='王五', age=23, status=VOCATION}] FREE=[Employee{name='张三', age=20, status=FREE}, Employee{name='赵六', age=23, status=FREE}] */ ---------------------------------------------------------------------------------------------------------- // 多级分组(先按状态分,再按姓名分) Map<Employee.Status, Map<String, List<Employee>>> collect6 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(Employee::getName))); collect6.forEach((x,y) -> { Map<String, List<Employee>> listMap = collect6.get(x); // x即为key System.out.println(x + "=" + listMap); listMap.forEach((a,b) -> { // a即为key List<Employee> employees = listMap.get(a); System.out.println(employees); }); }); /* 输出: BUSY={李四=[Employee{name='李四', age=18, status=BUSY}], 田七=[Employee{name='田七', age=25, status=BUSY}]} [Employee{name='李四', age=18, status=BUSY}] [Employee{name='田七', age=25, status=BUSY}] VOCATION={王五=[Employee{name='王五', age=23, status=VOCATION}]} [Employee{name='王五', age=23, status=VOCATION}] FREE={张三=[Employee{name='张三', age=20, status=FREE}], 赵六=[Employee{name='赵六', age=23, status=FREE}]} [Employee{name='张三', age=20, status=FREE}] [Employee{name='赵六', age=23, status=FREE}] */ ---------------------------------------------------------------------------------------------------------- // 统计 IntSummaryStatistics collect7 = employees.stream().collect(Collectors.summarizingInt(Employee::getAge)); System.out.println(collect7); // IntSummaryStatistics{count=5, sum=109, min=18, average=21.800000, max=25} System.out.println(collect7.getSum()); // 109 } ---------------------------------------------------------------------------------------------------------- // 分区 Map<Boolean,List<Employee>> map = employees.stream().collect(Collectors.partitioningBy(e -> e.getAge() > 20)); System.out.println(map); // 输出:{false=[Employee{name='张三', age=20, status=FREE}, Employee{name='李四', age=18, status=BUSY}], true=[Employee{name='王五', age=23, status=VOCATION}, Employee{name='赵六', age=23, status=FREE}, Employee{name='田七', age=25, status=BUSY}]} ---------------------------------------------------------------------------------------------------------- // 连接 String str = employees.stream().map(Employee::getName).collect(Collectors.joining(",", "<", ">")); System.out.println(str); // 输出:<张三,李四,王五,赵六,田七> }
-
以上就是 Stream 流的全部内容了,平时在工作中还是需要经常使用的,赶快收藏起来吧
-
创作不易,感谢阅读,若您喜欢这篇文章,不妨传承这份知识的力量,点个赞或关注我吧~
-
微信公众号:EzCoding