逸学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流是方便操作集合/数组的手段,集合/数组才是开发中的目的。

相关推荐
意疏4 分钟前
JDK动态代理、Cglib动态代理及Spring AOP
java·开发语言·spring
程序员_三木4 分钟前
使用 Three.js 创建圣诞树场景
开发语言·前端·javascript·ecmascript·three
小王努力学编程6 分钟前
【C++篇】AVL树的实现
java·开发语言·c++
找了一圈尾巴17 分钟前
Wend看源码-Java-集合学习(List)
java·学习
无名之逆18 分钟前
lombok-macros
开发语言·windows·后端·算法·面试·rust·大学期末
yuanbenshidiaos22 分钟前
C++-----图
开发语言·c++·算法
逊嘘37 分钟前
【Java数据结构】链表相关的算法
java·数据结构·链表
爱编程的小新☆38 分钟前
不良人系列-复兴数据结构(二叉树)
java·数据结构·学习·二叉树
m0_7482478042 分钟前
SpringBoot集成Flowable
java·spring boot·后端
小娄写码1 小时前
线程池原理
java·开发语言·jvm