Java8的Stream流详解

Stream流的概述

什么是Stream流?

在Java 8中,得益于Lambda所带来的函数式编程, 引入了一个全新的Stream流概念。

目的:用于简化集合和数组操作的API

Stream流式思想的核心:

1、先得到集合或者数组的Stream流(就是一根传送带)
2、把元素放上去
3、然后就用这个Stream流简化的API来方便的操作元素。

在这里插入图片描述在这里插入图片描述
问题1、Stream流的作用是什么,结合了什么技术?
简化集合、数组操作的API。结合了Lambda表达式

问题2、说说Stream流的思想和使用步骤。
1、先得到集合或者数组的Stream流(就是一根传送带)
2、把元素放上去
3、然后就用这个Stream流简化的API来方便的操作元素

Stream流的获取

Stream流的三类方法
1、获取Stream流——>创建一条流水线,并把数据放到流水线上准备进行操作.
2、中间方法——>流水线上的操作。一次操作完毕之后,还可以继续进行其他操作.
3、终结方法——>一个Stream流只能有一个终结方法,是流水线上的最后一个操作.

注:Stream操作集合或者数组的第一步是先得到Stream流,然后才能使用流的功能

集合获取Stream流的方式:可以使用Collection接口中的默认方法stream​()生成流

名称说明
default Stream<\E> stream()获取当前集合对象的Stream流

数组获取Stream流的方式

名称说明
public static <\T> Stream<\T> stream(\T[] array)获取当前数组的Stream流
public static <\T> Stream<\T> of(\T…values)获取当前数组/可变数据的Stream流

代码测试验证

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

public class StreamDemo2 {
    public static void main(String[] args) {
        /*------------------Collection集合获取流--------------------*/
        Collection<String> list = new ArrayList<>();  // 多态
        Stream<String> s = list.stream();

        /*------------------Map集合获取流--------------------*/
        Map<Integer, String> map = new HashMap<>();  // 多态

        // 获取键流
        Stream<Integer> keyStream = map.keySet().stream();  //  map.keySet(): 获取键的集合

        // 获取值流
        Stream<String> valueStream = map.values().stream();  //  map.values(): 获取值的集合

        // 获取键值对流
        Stream<Map.Entry<Integer, String>> keyAndValueStream = map.entrySet().stream();  // map.entrySet(): 获取键值对的集合

        /*------------------数组获取流--------------------*/
        String[] names = {"1", "2"};
        // 1.1 数组获取流的方式
        Stream<String> namesStream = Arrays.stream(names);
        // 1.2 数组获取流的方式
        Stream<String> stringStream = Stream.of(names);
    }
}

Stream流的常用API

Stream流的常用API(中间操作方法)
在这里插入图片描述注意:
1、中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程
2、在Stream流中无法直接修改集合、数组中的数据

Stream流的常见终结操作方法

名称说明
void forEach(Consumer action)对此流的每个元素执行遍历操作
long count()返回此流中的元素数

注意终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了

问题1、终结和非终结方法的含义是什么

终结方法后流不可以继续使用,非终结方法会返回新的流,支持链式编程。

代码测试验证

public class StreamDemo3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        // 1. filter : 过滤元素
        //Stream<T> filter(Predicate<? super T> predicate);
        // 链式编程,输出结果:张无忌 张强 张三丰 张三丰
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));

        // count:统计个数,
        long size = list.stream().filter(s -> s.length() == 2).count();  // 返回值为long类型而不是int类型
        System.out.println(size);  // 2

        // limit : 取前几个元素
        // 链式编程,输出结果:张无忌 张强
        list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));

        // skip : 跳过前几个
        // 链式编程,输出结果:张三丰 张三丰
        list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(s -> System.out.println(s));

        // map : 加工方法, 第一个参数原材料 --> 第二个参数是加工后的结果
        // 链式编程,输出结果:
        /**
         * xyz张无忌
         * xyz周芷若
         * xyz赵敏
         * xyz张强
         * xyz张三丰
         * xyz张三丰
         */
        list.stream().map(s -> "xyz" + s). forEach(s -> System.out.println(s));

        // 把所有的名称都加工成一个学生对象
        list.stream().map(s -> new Student(s)).forEach(student -> System.out.println(student));
        list.stream().map(Student::new).forEach(System.out::println);  // 构造器引用   方法引用(要求前后参数一致)

        // concat : 合并流。
        // 链式编程,输出结果:
        /**
         * 周芷若
         * Java1
         * Java2
         */
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("周"));
        Stream<String> s2 = Stream.of("Java1", "Java2");
        Stream<String> s3 = Stream.concat(s1, s2);
        s3.forEach(s -> System.out.println(s));
    }
}

Stream流的综合应用

案例需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。

分析:
1、员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
2、开发一部有4个员工、开发二部有5名员工
3、分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
4、分别统计出2个部门的平均月收入,要求去掉最高和最低工资。
5、统计2个开发部门整体的平均工资,去掉最低和最高工资的平均值。

Employee类

public class Employee {
    private String name;
    private char sex;
    private double salary;
    private double bonus;
    private String punish;  // 处罚记录,如果没有则为null

    public Employee() {
    }

    public Employee(String name, char sex, double salary, double bonus, String punish) {
        this.name = name;
        this.sex = sex;
        this.salary = salary;
        this.bonus = bonus;
        this.punish = punish;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public String getPunish() {
        return punish;
    }

    public void setPunish(String punish) {
        this.punish = punish;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", punish='" + punish + '\'' +
                '}';
    }
}

Topperformer类:

public class Topperformer {
    private String name;
    private double money;

    public Topperformer(){}

    public Topperformer(String name, double money){
        this.name = name;
        this.money = money;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Topperformer{" +
                "name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

测试StreamDemo类:

public class StreamDemo04 {
    public static double allMoney = 0;
    public static void main(String[] args) {
        List<Employee> one = new ArrayList<>();
        one.add(new Employee("猪八戒",'男',30000 , 25000, null));
        one.add(new Employee("孙悟空",'男',25000 , 1000, "顶撞上司"));
        one.add(new Employee("沙僧",'男',20000 , 20000, null));
        one.add(new Employee("小白龙",'男',20000 , 25000, null));

        List<Employee> two = new ArrayList<>();
        two.add(new Employee("武松",'男',15000 , 9000, null));
        two.add(new Employee("李逵",'男',20000 , 10000, null));
        two.add(new Employee("西门庆",'男',50000 , 100000, "被打"));
        two.add(new Employee("潘金莲",'女',3500 , 1000, "被打"));
        two.add(new Employee("武大郎",'女',20000 , 0, "下毒"));

        Employee oneMax = one.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).get();
        System.out.println(oneMax);

        Topperformer oneTopperformer = one.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).map(
                employee -> new Topperformer(employee.getName(), employee.getBonus() + employee.getSalary())
        ).get();
        System.out.println(oneTopperformer);

        Employee twoMax = two.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).get();
        System.out.println(twoMax);

        Topperformer twoTopperformer = two.stream().max((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).map(
                employee -> new Topperformer(employee.getName(), employee.getBonus() + employee.getSalary())
        ).get();
        System.out.println(twoTopperformer);

        System.out.println("----------------------------------------------------------------");
        one.stream().sorted((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).  // 升序
                skip(1).limit(one.size()-2).forEach(employee -> {
                    allMoney += (employee.getBonus() + employee.getSalary());
        });
        System.out.println("部门1的平均工资为:" + allMoney / (one.size() - 2));

        allMoney = 0;
        two.stream().sorted((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).
                skip(1).limit(two.size() - 2).forEach(employee -> {
                    allMoney += (employee.getBonus() + employee.getSalary());
                });
        System.out.println("部门2的平均工资为:" + allMoney / (two.size() - 2));
        System.out.println("----------------------------------------------------------------");

        allMoney = 0;
        Stream<Employee> s1_stream = one.stream();  // 需要指定泛型,如果不指定返回的就死Object类型
        Stream<Employee> s2_stream = two.stream();
        Stream<Employee> s3 = Stream.concat(s1_stream, s2_stream); // 合并两个流
        s3.sorted((o1, o2) -> Double.compare(o1.getBonus() + o1.getSalary(), o2.getSalary() + o2.getBonus())).
                skip(1).limit(one.size() + two.size() - 2).forEach(employee -> {
                    allMoney += (employee.getBonus() + employee.getSalary());
                });

        // BigDecimal精度问题
        BigDecimal a = BigDecimal.valueOf(allMoney);
        BigDecimal b = BigDecimal.valueOf(one.size() + two.size() - 2);
        System.out.println("开发部门平均工资为:" + a.divide(b, 4, RoundingMode.HALF_UP));
    }
}

收集Stream流

Stream流的收集操作
收集Stream流的含义就是把Stream流操作后的结果数据转回到集合或者数组中去

Stream流:方便操作集合/数组的手段。
集合/数组:才是开发中的目的。

Stream流的收集方法:

名称说明
R collect(Collector collector)开始收集Stream流,指定收集器

Collectors工具类提供了具体的收集方式

名称说明
public static <\T> Collector toList()把元素收集到List集合中
public static <\T> Collector toSet()把元素收集到Set集合中
public static <\T> Collector toMap()把元素收集到Map集合中

代码测试验证

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 目标:收集Stream流的数据到 集合或者数组中去。
 */
public class StreamDemo5 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();  // 多态的写法

        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        List<String> zhangList = s1.collect(Collectors.toList());  // 可变集合
        System.out.println(zhangList);  // [张无忌, 张强, 张三丰, 张三丰]
        zhangList.add("赵六");
        System.out.println(zhangList);  // [张无忌, 张强, 张三丰, 张三丰, 赵六]

        // stream has already been operated upon or closed
        // 特别注意注意注意: stream流只能用一次,要不然会报错:stream has already been operated upon or closed
        Stream<String> s2 = list.stream().filter(s -> s.startsWith("赵"));
        List<String> luList = s2.collect(Collectors.toList());
        System.out.println(luList);  // [赵敏]


//        List<String> list1 = s1.toList();  // 直接进行toList为不可变集合,只能进行查看操作,不能进行增加,删除,更新操作
//        System.out.println(list1);
//        list1.add("杨银羽");
        System.out.println("----------------------------------------------------");
        Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
        Set<String> zhangSet = s3.collect(Collectors.toSet());
        System.out.println(zhangSet);  // [张强, 张三丰, 张无忌]
        System.out.println("----------------------------------------------------");
        Stream<String> s4 = list.stream().filter(s -> s.startsWith("张"));
        Object[] objects = s4.toArray();
        System.out.println("数组内容为:" + Arrays.toString(objects));  // 数组内容为:[张无忌, 张强, 张三丰, 张三丰]
    }
}

版权声明:本文为qq_43751200原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。