逸学java【初级菜鸟篇】9.3 Stream流

hi,我是逸尘,一起学java吧


得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念(就是都基本使用lambda的形式)。

流处理

我们首先理解什么是流处理,它类似于sql语句,可以执行非常复杂的过滤,映射,查找,收集等功能,且代码很少,但是可读性不高。字如其名,它的处理如同流淌的水一样,或者可以理解为流水线一样。

Stream流

Stream流也是流处理的一种,大多数流处理都是在Stream接口处理的,它是一个泛接口,所以它可以操作的元素是任意对象,他的操作可以用lambda去书写(推荐)。

生成Stream流

Stream操作集合和数组的第一步是得到(生成)Stream流。

在Collection接口默认方法是stream()生成流。

在数组中使用Arrays.stream(数组) /或Stream.of(数组);

中间操作方法

其次我们就可以使用中间操作来处理这些元素对象。

这里举出一些常见的API

  • forEach : 逐一处理(遍历)
  • count:统计个数
  • filter : 过滤元素 【数据过滤】
  • distinct:去除重复元素 【数据过滤】
  • limit : 取前几个元素 【数据过滤】
  • skip : 跳过前几个 【数据过滤】
  • map : 加工方法 【数据映射】
  • allMatch:判断流中的元素是否会全部符合某一个条件 【数据查找】
  • concat:合并流

终结操作方法

终结操作方法调用以后流就无法使用了它是流的最后一个过程。

常见的有API有

单独保存的操作方法

  • collect() 方法配合collectors类将流的结果进行保存

处了stream流本身的方法我们还有两个可以协助流操作的类

Collectors类

collectors是一个收集器类,可以将Stream流对象进行封装,归集,分组,是数据的收集,筛选出特殊的数据,可以复杂的统计。

1.toList()将流元素封装到List集合 toSet() toMap()类似

2.toCollection(Supplier<C> collectionFactory) 将流中的元素收集到指定类型的集合中的方法

即一个类型为 Supplier<C> 的函数式接口,其中 C 是要创建的集合类型。例如,如果我们想要创建一个 LinkedList 集合,可以这样使用该方法:

java 复制代码
List<Integer> list = Stream.of(1, 2, 3, 4, 5)
    .collect(Collectors.toCollection(LinkedList::new));

3.groupingBy(Function<? super T, ? extends K> classifier)

  • 一个函数式接口 classifier,表示如何对流中的元素进行分类。

例如,我们有一个字符串列表,并希望按照字符串长度分组:

java 复制代码
List<String> list = Arrays.asList("apple", "banana", "peach", "grape","pear");
Map<Integer, List<String>> map = list.stream().collect(Collectors.groupingBy(String::length));

在上面的代码中,我们使用 String::length 函数式接口将字符串转换为它的长度,并将其作为分类键。运行结果如下:

{4=pear, 5=apple, peach,grape, 6=banana}

需要注意的是,groupingBy() 方法返回的是一个 Map 对象,其中键是分类键.

Collectors.groupingBy() 方法还提供了第二个参数 downstream,用于进一步对分组的结果进行处理。例如,我们可以使用 Collectors.counting() 方法统计每个分组中元素的数量:

java 复制代码
Map<Integer, Long> map = list.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.counting()));

上面的代码中,我们使用 Collectors.counting() 方法作为 downstream 参数,统计了每个分组中元素的数量,并将结果封装为 Long 类型。运行结果如下:

{4=1, 5=3, 6=1}

4.toConcurrentMap

将流中的元素收集到一个并发 Map 中的方法

java 复制代码
ConcurrentMap<Integer, String> concurrentMap = Stream.of("a", "b", "c")
    .collect(Collectors.toConcurrentMap(
        String::length,
        Function.identity()
    ));

在上面的例子中,我们使用 toConcurrentMap() 方法创建了一个并发 Map,它将字符串的长度作为键,字符串本身作为值。具体来说,这个方法接受两个参数:

一个函数式接口 keyMapper,表示如何将流中的元素转换成键;

一个函数式接口 valueMapper,表示如何将流中的元素转换成值。

对于上述例子中的流,String::length 函数式接口将字符串转换成它的长度,而 Function.identity() 函数式接口则将字符串映射成它本身。因此得到的结果为:

{1=a, 2=b, 3=c}

5.以及averagingDouble计算元素平均值,maxBy返回符合条件的最大值,joining()按顺序将元素连接成一个String类型数据,counting()统计个数等等

optional类

这是一个容器类

它的主要功能是针对NullpointerException空指针异常做处理,可以保证保存的值不为null。

of()返回一个value值等于参数的optional实例

ofNullable()是返回一个value值等于非null参数的optional实例

filter()是给定条件值匹配

empty()是静态方法,返回一个空值的optional实例

案例

我们上面的中间操作,其实是对我们的数据源进行加工。

这里我们简单做了一个去重

java 复制代码
package com.yd.yc;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Thirteen {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(6,88,4,5,22,22,6,66,7);
        //原数据
        printeach(list);
        //获取stream,去重,收集器类重新封装
        List<Integer> collect = list.stream().distinct().collect(Collectors.toList());
        printeach(collect);
    }
        //遍历方法
    private static void printeach(List<Integer> list) {
        System.out.println("集合内容"+list);
        //逐一处理(遍历)里面是lambda
        list.stream().forEach(n-> System.out.println(n+""));

    }
}

某个公司的部门,分为开财务部门和开发部门,现在需要进行月中数据结算。

创建一张员工

部门 姓名 年龄 月工资 性别
开发部 张三 28 15000 男
开发部 李四 35 20000 女
开发部 王五 29 18000 男
财务部 赵六 33 16000 女
财务部 刘七 30 17000 男
财务部 陈八 27 14000 女

对应的是我们的员工实体类

java 复制代码
public class Employee {  
    private String name;  
    private int age;  
    private double monthlySalary;  
    private String gender;  
    private String department;  
  
    public Employee(String name, int age, double monthlySalary, String gender, String department) {  
        this.name = name;  
        this.age = age;  
        this.monthlySalary = monthlySalary;  
        this.gender = gender;  
        this.department = department;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public int getAge() {  
        return age;  
    }  
  
    public double getMonthlySalary() {  
        return monthlySalary;  
    }  
  
    public String getGender() {  
        return gender;  
    }  
  
    public String getDepartment() {  
        return department;  
    }  
}

分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象topperformer

方案A

java 复制代码
package com.yd.yc;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class EmployeeTest {
    public static void main(String[] args) {
        Employee employee1 = new Employee("张三", 28, 15000, "男", "开发部");
        Employee employee2 = new Employee("李四", 35, 20000, "女", "开发部");
        Employee employee3 = new Employee("王五", 29, 18000, "男", "开发部");
        Employee employee4 = new Employee("赵六", 33, 16000, "女", "财务部");
        Employee employee5 = new Employee("刘七", 30, 17000, "男", "财务部");
        Employee employee6 = new Employee("陈八", 27, 44000, "女", "财务部");
        //测试
        //System.out.println(employee1.getName());  // 输出:张三
        //System.out.println(employee2.getMonthlySalary());  // 输出:20000.0
        ArrayList<Employee> employeeList = new ArrayList<>();
        employeeList.add(employee1);
        employeeList.add(employee2);
        employeeList.add(employee3);
        employeeList.add(employee4);
        employeeList.add(employee5);
        employeeList.add(employee6);

        //A方案
        //filter是过滤找到符合条件的元素
        //Collectors.maxBy去返回符合条件的最大值,Comparator.comparing(param),param : 这个参数是Function函数式对象,默认大,Comparator.reverseOrder()默认倒序
        Optional<Employee> result = employeeList.stream()
                .filter(e -> "开发部".equals(e.getDepartment()))
                .collect(Collectors.maxBy(Comparator.comparing(Employee::getMonthlySalary)));
        //返回一个实体类对象
        Employee employee = result.get();
        System.out.println(employee.getName());

        //第二种写法
        Employee  resultOne= employeeList.stream().filter(e -> "财务部".equals(e.getDepartment()))
                .max((o1, o2) -> Double.compare(o1.getMonthlySalary(), o2.getMonthlySalary())).get();
        //必须重写toString才可以有内容
        System.out.println(resultOne);
        System.out.println(resultOne.getMonthlySalary());
        //包装在一个优秀员工里
        List<Employee> topEmployees = new ArrayList<>();
        topEmployees.add(resultOne);
        topEmployees.add(employee);
        System.out.println(topEmployees);
        
    }
}

方案B

java 复制代码
        //B方案
        Map<String, List<Employee>> groupedByDepartment = employeeList.stream()
                //分组
                .collect(Collectors.groupingBy(Employee::getDepartment));


        List<Employee> topEmployees = new ArrayList<>();

        for (List<Employee> departmentEmployees : groupedByDepartment.values()) {
            Employee topEmployee = departmentEmployees.stream()
                    .max(Comparator.comparingDouble(Employee::getMonthlySalary))
                    //注意,如果一个部门没有员工,那么这个方法将返回null
                    //该方法在给定的流中找不到元素时返回一个默认值。
                    .orElse(null);
            if (topEmployee != null) {
                topEmployees.add(topEmployee);
            }
        }
        System.out.println(topEmployees);

我们要注意的是stream流是方便操作集合/数组的手段,集合/数组才是开发中的目的。

相关推荐
Dreamboat-L1 小时前
日志级别基础 + Spring Boot 日志实践指南
java·服务器·数据库·java-ee·springboot
啵啵啵鱼1 小时前
第二章 线性表
java·数据结构·算法
微三云 - 廖会灵 (私域系统开发)1 小时前
供应链系统实战:多渠道订单聚合与分布式库存一致性方案总结
开发语言·分布式·php
深入云栈1 小时前
Netty 4.2.x 源码深度解析 (十九):ChannelPool 连接池 —— 连接复用与资源管理
java·后端·架构
用户094248568031 小时前
第38章:从零构建JVM稳定性平台最小闭环
java·jvm
Zhuo1Joy2 小时前
压测全绿的假象:0% 错误率的系统与 17.2% 成功率的业务
java
学逆向的2 小时前
C 数据溢出与精度丢失
c语言·开发语言·网络安全
silvamux_2 小时前
Spring Boot 调用大模型 api 最小可跑教程
java·spring boot·后端
今年下半年2 小时前
【Spring Boot 】通过 OpenFeign 请求第三方接口
java·spring boot·后端·openfeign
三翼鸟数字化技术团队2 小时前
Springboot + WebFlux + Disruptor 实现数据转发
java·spring boot