这样用它才叫优雅!

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

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值