• Java8-Stream流详细教程


    前言

    1、什么是Stream

    前面我们讲了Lambda表达式与Optional类,下面我们将会使用这两个新特性,特别是Lambda
    Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列

    集合讲的是数据,Stream讲的是计算!

    注意:

    • Stream自己不会存储元素
    • Stream不会改变源对象。相反,他们会返回一个特有结果的新Stream
    • Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

    2、为什么要使用StreamAPI

    实际开发中,项目多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理

    Stream和Collection集合的区别:==Collectioin是一种静态的内存数据结构,而Stream是有关计算的。==前者主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。

    2.1、举例

    假设你有一个班所有学生的分数数据,你需要从中筛选出成绩不及格的,并且按照分数排序,打印按顺序学生的名称

    JDK1.7写法
    package com.tcc.test;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    
    /**
     * @author 宇辰
     * @date 2022/8/31-8:53
     **/
    public class Test{
        public static void main(String[] args){
            // 元数据
            List<Student> students = Arrays.asList(
                    new Student("张三", 20, "100班", 56D),
                    new Student("李四", 23, "101班", 44D),
                    new Student("王五", 25, "102班", 44.5D)
            );
    
            ArrayList<Student> list = new ArrayList<>();
            // 筛选出所有成绩不合格的学生
            for (Student s : students) {
                if (s.getScore() < 60){
                    list.add(s);
                }
            }
    
            // 排序
            Comparator com = new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    Student stu1 = (Student) o1;
                    Student stu2 = (Student) o2;
    
                    // 按照分数排序
                    return stu1.getScore().compareTo(stu2.getScore());
                }
            };
    
            list.sort(com);
    
            // 遍历得出所有名称
            /*
                李四
                王五
                张三
             */
            for (Student s : list) {
                System.out.println(s.getName());
            }
    
        }
    
    }
    
    class Student{
        // 姓名
        private String name;
        // 年龄
        private Integer age;
        // 班级
        private String clazz;
        // 分数
        private Double score;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getClazz() {
            return clazz;
        }
    
        public void setClazz(String clazz) {
            this.clazz = clazz;
        }
    
        public Double getScore() {
            return score;
        }
    
        public void setScore(Double score) {
            this.score = score;
        }
    
        public Student() {
        }
    
        public Student(String name, Integer age, String clazz, Double score) {
            this.name = name;
            this.age = age;
            this.clazz = clazz;
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", clazz='" + clazz + '\'' +
                    ", score=" + score +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    JDK1.8写法
    public class Test{
        public static void main(String[] args){
            // 元数据
            List<Student> students = Arrays.asList(
                    new Student("张三", 20, "100班", 56D),
                    new Student("李四", 23, "101班", 44D),
                    new Student("王五", 25, "102班", 44.5D)
            );
    
            /*
                李四
                王五
                张三
             */
            students.stream()
                	// 中间操作,过滤小于60分的同学
                    .filter(s -> s.getScore() < 60)
                	// 中间操作,定制排序
                    .sorted((s1,s2) -> Double.compare(s1.getScore(),s2.getScore()))
                	// 中间操作,依次获取学生的姓名
                    .map(s -> s.getName())
                	// 终止操作,内部迭代打印获取到的每个学生的姓名
                    .forEach(
                            System.out::println
                    );
        }
    
    }
    
    ....省略Student
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.2、执行步骤

    在这里插入图片描述

    3、StreamAPI说明

    • Java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个是Stream API
    • ==Stream API(java.util.stream)==把真正的函数式编程风格引入到了Java中。这是目前为止对Java类库最好的补充,因为StreamAPI可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
    • Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非重复杂的查找、过滤和映射数据等操作。使用==Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。==也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式

    4、Stream的操作三个步骤

    1、创建Stream

    一个数据源(如:集合、数组),获取一个流

    2、中间操作

    一个中间操作链,对数据源的数据进行处理

    3、终止操作(终端操作)

    一旦执行终止操作,就执行中间操作链,并产生结果:之后,不会再被使用

    在这里插入图片描述

    5、并行流与串行流

    并行流:就是把一个内容分成多个数据块,并用不用的线程分别处理每个数据块的。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。

    Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。

    StreamAPI可以声明性地通过parallel()sequential()在并行流与顺序流之间进行切换

    创建Stream流方式

    以下所有演示代码都使用了Lambda表达式,如果不了解的话,可以阅读:Lamdba表达式快速学习与使用

    1、通过集合

    Java8 中的Collection接口被扩展,提供了两个获取流的方法:

    • default Stream stream():返回一个顺序流
    • default Stream parallelStream():返回一个并行流
    public static void main(String[] args){
        ArrayList<String> list = new ArrayList<>();
        // 创建一个串行流
        Stream<String> stream = list.stream();
    
        // 创建一个并行流
        Stream<String> parallelStream = list.parallelStream();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、通过数组

    Java8 中的Arrays 的静态方法stream()可以获取数组流:

    • seatic Stream stream(T[] array):返回一个流

    重载形式,能够处理对应基本类型的数组:

    • public static IntStream stream(int[] array)
    • public static LongStream stream(logn[] array)
    • public static DoubleStream stream(double[] array)
    public static void main(String[] args){
        String[] s = new String[16];
        // 串行流,没有提供创建并行流的方法
        Stream<String> str1 = Arrays.stream(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、通过Stream的of()

    可以调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数

    • public static Stream of(T… values):返回一个流
    public static void main(String[] args){
        Stream<Integer> stream = Stream.of(123, 222, 344);
    }
    
    • 1
    • 2
    • 3

    4、由文件生产流

    Java中用于处理文件等I/O操作的NIO API(非阻塞I/O)已更新,以便利用Stream API。java.nio.file.Files中的很多静态方法都会返回一个流。例如,一个很有用的方法:Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

    例子:差异一个文件中有多少各不相同的词

    public static void main(String[] args){
        // 这样写会自动关闭流
        try (
            Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
        ){
            // 需要用flatMap方法扁平化返回值
            lines.flatMap(line -> Arrays.stream(line.split(" ")))
                // 中间操作,去重
                .distinct()
                // 终止操作,计数
                .count();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5、创建无限流

    可以使用静态方法Stream.iterate()和Stream.generate(),创建无限流。

    • 迭代
      • public static Stream iterate(final T seed,final UnaryOperator f)
    • 生成
      • public static Stream generate(Supplier s)

    5.1、iterate

    public static void main(String[] args){
        Stream<Integer> iterate = Stream.iterate(0, t -> t + 2);
    
        // 中间操作,后面会讲到,用于截断流,取前三个
        Stream<Integer> limit = iterate.limit(3);
        /*
                0
                2
                4
             */
        // 终止操作 内部迭代
        limit.forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.2、generate

    public static void main(String[] args){
        Stream<Double> generate = Stream.generate(Math::random);
    
        Stream<Double> limit = generate.limit(3);
        /*
                0.49171014974935257
                0.7073185115880065
                0.5253605859404055
             */
        limit.forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    中间操作

    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,成为"惰性求值"

    1、筛选与切片

    • filter:接收Lambda,从流汇总排除某些元素
    • distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素
    • limit(long maxSize):截断流,使其元素不超过给定数量
    • skip(long n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补

    1.1、filter

    该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流

    比如说:你想获取所有成绩不及格的学生

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            // 如果满足条件,就继续执行。不满足条件的剔除
            .filter(s -> s.getScore() < 60)
            /*
                        Student{name='李四', age=21, clazz='101班', score=50.0}
                        Student{name='王五', age=22, clazz='102班', score=44.5}
                        Student{name='赵六', age=20, clazz='103班', score=44.5}
                     */
            .forEach(
                System.out::println
            );
    }
    
    // ...Student类定义省略,前言第二部分举例里面有。。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    1.2、distinct

    去重,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流

    比如说:你想知道同学的年龄都在哪个年龄段上有

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            // 获取所有同学的年龄
            .map(Student::getAge)
            // 去重
            .distinct()
            /*
                        20
                        21
                        22
                     */
            .forEach(
                System.out::println
            );
    }
    
    // ...Student类定义省略,前言第二部分举例里面有。。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    1.3、limit

    截短流,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。

    如果流式有序的,则最多会返回前n个元素。

    比如说:你想获取成绩不及格的前两个人

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            // 如果满足条件,就继续执行。不满足条件的剔除
            .filter(s -> s.getScore() < 60)
            .limit(2)
            /*
                    Student{name='李四', age=21, clazz='101班', score=50.0}
                    Student{name='王五', age=22, clazz='102班', score=44.5}
                     */
            .forEach(
                System.out::println
            );
    }
    
    // ...Student类定义省略,前言第二部分举例里面有。。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    1.4、skip

    流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。

    注意:limit(n)和skip(n)是互补的:

    比如说:你想获取除了前两个后面的所有不及格的学生

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            // 如果满足条件,就继续执行。不满足条件的剔除
            .filter(s -> s.getScore() < 60)
            .skip(2)
            // Student{name='赵六', age=20, clazz='103班', score=44.5}
            .forEach(
            System.out::println
        );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    2、映射

    • map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    • flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
    • mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream
    • mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream
    • mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream

    2.1、map

    流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一次,是因为它和转换类似,但其中的细微差别在于,它是"创建一个新的版本",而不是去"修改"原来的数据)

    比如说:你想获取所有学生的姓名

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            .map(Student::getName)
            /*
                        李四
                        王五
                        赵六
                        田七
                     */
            .forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    比如说:你想再获取每个人名字有多长

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            .map(Student::getName)
            .map(String::length)
            /*
                        2
                        2
                        2
                        2
                     */
            .forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.2、flatMap

    flatMap方法让你把一个流中的每个值都换成另一个流,然后把所有流链接起来成为一个流

    比如说:你想获取所有人名字不相同的字有哪些,我们先来使用map看看会有什么问题

    2.1、使用map
    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("张四", 21, "101班", 50D),
            new Student("李三", 20, "103班", 44.5D)
        );
    
        // 你会发现它返回的不是你想要的Stream,而是里面包含了一层String[]
        Stream<String[]> stream = students.stream()
            .map(Student::getName)
            .map(str -> str.split(""))
            .distinct();
    
        // 这时候你需要在forEach里面再遍历strings数组,并没有达到去重的效果
        /*
                张
                三
                张
                四
                李
                三
             */
        stream.forEach(
            strings -> {
                for (String s : strings) {
                    System.out.println(s);
                }
            }
        );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    2.2、原因

    在这里插入图片描述

    2.3、使用flatMap

    我们可以使用flatMap来简化上面代码

    1. 我们使用split方法获取到的是一个数组,我们上面学习到,可以使用Arrays.stream()方法,把数组转换为一个Stream流
    2. 转换为流后,我们的返回值就会为Stream>,这时候我们使用flatMap方法,把这个流中流,转换为单个流Stream

    实践如下

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("张四", 21, "101班", 50D),
            new Student("李三", 20, "103班", 44.5D)
        );
        students.stream()
            .map(Student::getName)
            .flatMap(str -> Arrays.stream(str.split("")))
            .distinct()
            /*
                        张
                        三
                        四
                        李
                     */
            .forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.4、执行流程如下

    在这里插入图片描述

    2.3、数值流

    想IntSteam、DoubleStream、LongStream都属于数值流,我们会放到终止操作的第二章(归约)后面详细讲解

    3、排序

    • sorted():产生一个新流,其中按自然顺序排序
    • sorted(Comparator com):产生一个新流,其中按比较器顺序排序

    3.1、sorted()

    根据元素类型内置的排序规则进行排序,默认升序排序

    比如说:根据每个学生的分数进行排序

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            .map(Student::getScore)
            // 根据Double类内置的compareTo方法进行排序
            .sorted()
            /*
                        44.5
                        44.5
                        50.0
                        88.0
                        91.0
                     */
            .forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.2、sorted(Comparator com)

    根据自定义排序规则进行排序

    比如说:根据每个学生分数进行排序,如果分数相同,则根据年龄排序

    // 元数据
    List<Student> students = Arrays.asList(
        new Student("张三", 20, "100班", 88D),
        new Student("李四", 21, "101班", 50D),
        new Student("王五", 22, "102班", 44.5D),
        new Student("赵六", 20, "103班", 44.5D),
        new Student("田七", 21, "103班", 91D)
    );
    students.stream()
        .sorted((s1,s2) -> {
            int i = s1.getScore().compareTo(s2.getScore());
            if(i == 0){
                // 如果需要降序排序,前面加个 - 号即可
                return s1.getAge().compareTo(s2.getAge());
            }
            // 如果需要降序排序,前面加个 - 号即可
            return i;
        })
        /*
                        Student{name='赵六', age=20, clazz='103班', score=44.5}
                        Student{name='王五', age=22, clazz='102班', score=44.5}
                        Student{name='李四', age=21, clazz='101班', score=50.0}
                        Student{name='张三', age=20, clazz='100班', score=88.0}
                        Student{name='田七', age=21, clazz='103班', score=91.0}
                     */
        .forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    终止操作

    终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。

    流进行了终止操作后,不能再次使用。可以再次生成一个新流进行使用

    1、匹配与查找

    • allMatch(Predicate p):检查是否匹配所有元素
    • anyMatch(Predicate p):检查是否至少匹配一个元素
    • noneMatch(Predicate p):检查是否没有匹配所有元素
    • findFirst():返回第一个元素
    • findAny():返回当前流中的任意元素
    • count():返回流中元素总数
    • max(Comparator c):返回流中最大值
    • min(Comparator c):返回流中最小值
    • forEach(Consumer c):内部迭代(使用Collection接口需要用户去做迭代,成为外部迭代。相反,StreamAPI使用内部迭代———它帮你把迭代做了)

    1.1、allMatch

    检测流中所有元素是否都能匹配给定的谓词

    比如说:你想查看是否所有学生成绩都及格了

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
        boolean b = students.stream()
            .allMatch(s -> s.getScore() >= 60);
        System.out.println(b); // false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.2、anyMatch

    检测流中是否有一个元素能匹配给定的谓词

    比如说:你想查看是否有一个同学不及格

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
        boolean b = students.stream()
            .anyMatch(s -> s.getScore() < 60);
        System.out.println(b); // true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.3、noneMatch

    allMatch相反,检测流中所有元素是否都不能匹配给定的谓词

    比如说:你想看看是否所有的学生都不是105班的

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
        boolean b = students.stream()
            .noneMatch(s -> "105".equals(s.getClazz()));
        System.out.println(b); // true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.4、findFirst

    有些流使用排序来指定集合中的顺序,对于这种元素,你可能想要找到第一个元素。

    比如说:找到分数第一的学生

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 使用Optional可以防止找不到,空指针异常
        Optional<Student> student = students.parallelStream()
            // 使用Comparator的默认方法,reversed方法用于反转顺序,默认升序排序,需要降序排序,然后拿到第一名的学生
            .sorted(Comparator.comparingDouble(Student::getScore).reversed())
            .findFirst();
    
        System.out.println(student); // Optional[Student{name='田七', age=21, clazz='103班', score=91.0}]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.5、findAny

    findAny方法将返回当前流中任意元素。可以与其他流操作结合使用

    比如说:你可能想抽取103班的任意一人

    public static void main(String[] args){
            // 元数据
            List<Student> students = Arrays.asList(
                    new Student("张三", 20, "100班", 88D),
                    new Student("李四", 21, "101班", 50D),
                    new Student("王五", 22, "102班", 44.5D),
                    new Student("赵六", 20, "103班", 44.5D),
                    new Student("田七", 21, "103班", 91D)
            );
    
            // 使用Optional可以防止找不到,空指针异常
        	// 使用并行流可以直观看出(多运行几次,查看结果)
            Optional<Student> student = students.parallelStream()
                    .filter(s -> "103班".equals(s.getClazz()))
                    .findAny();
    
            System.out.println(student); // Optional[Student{name='赵六', age=20, clazz='103班', score=44.5}]
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.6、count

    比如说:你想知道有几个学生不及格

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
    
        long count = students.stream()
            .filter(s -> s.getScore() < 60)
            .count();
        System.out.println(count); // 3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.7、max

    比如说:你想知道最高分是多少

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 从1-100的所有偶数个数
        Optional<Double> max = students.stream()
            .map(Student::getScore)
            .max(Double::compare);
        System.out.println(max); // Optional[91.0]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.8、min

    比如说:你想知道最低分是多少

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 从1-100的所有偶数个数
        Optional<Double> max = students.stream()
            .map(Student::getScore)
            .min(Double::compare);
        System.out.println(max); // Optional[44.5]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.9、forEach

    比如说:你想打印成绩不及格的学生姓名

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        students.stream()
            .filter(s -> s.getScore() < 60)
            /*
                        李四
                        王五
                        赵六
                     */
            .forEach(s -> System.out.println(s.getName()));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2、归约

    • reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T
      • 参数一:变量的初始值
      • 参数二:将列表中所有元素结合在一起的操作
    • reduce(BinaryOperator b):可以将流中元素反复结合起来,的达到一个值。返回Optional

    map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名

    2.1、reduce(T iden, BinaryOperator b)

    总和:比如说:你想知到所有学生的总分是多少

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 使用Optional可以防止找不到,空指针异常
        Double reduce = students.stream()
            // 获取所有同学的分数
            .map(Student::getScore)
            //                .reduce(0D, (a, b) -> a + b);
            // 优化,Double有一个sum的静态方法,恰巧可以让我们使用方法引用
            .reduce(0D,Double::sum);
        System.out.println(reduce); // 318.0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    最大值:比如说:你想知道最高分是多少

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 使用Optional可以防止找不到,空指针异常
        Optional<Double> reduce = students.stream()
            .map(Student::getScore)
            //                .reduce((a, b) -> a>b?a:b);
            // 优化,Double有一个max的静态方法,恰巧可以让我们使用方法引用
            .reduce(Double::max);
        System.out.println(reduce); // Optional[91.0]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    最小值:比如说:你想知道最低分是多少

    public static void main(String[] args){
            // 元数据
            List<Student> students = Arrays.asList(
                    new Student("张三", 20, "100班", 88D),
                    new Student("李四", 21, "101班", 50D),
                    new Student("王五", 22, "102班", 44.5D),
                    new Student("赵六", 20, "103班", 44.5D),
                    new Student("田七", 21, "103班", 91D)
            );
    
            // 使用Optional可以防止找不到,空指针异常
            Optional<Double> reduce = students.stream()
                    .map(Student::getScore)
    //                .reduce((a, b) -> a>b?b:a);
                    // 优化,Double有一个min的静态方法,恰巧可以让我们使用方法引用
                    .reduce(Double::min);
            System.out.println(reduce); // Optional[44.5]
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.2、reduce(BinaryOperator b)

    为什么这个方法的返回值会是Optional类型的呢,考虑到流中没有任何元素的情况,reduce无法返回其和,因为它没有初始值

    比如说:你想知到所有学生的总分是多少

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 使用Optional可以防止找不到,空指针异常
        Optional<Double> reduce = students.stream()
            .map(Student::getScore)
            //                .reduce((a, b) -> a + b);
            // 优化,Double有一个sum的静态方法,恰巧可以让我们使用方法引用
            .reduce(Double::sum);
        System.out.println(reduce); // Optional[318.0]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、map补充-数值流

    • 经过上面的归约计算求和,我们回想,如果直接有一个sum()的终端操作,不是更好,就不需要调用Double里面的方法了。

    • 但这是不可能的。问题在于map方法会生成一个Stream。虽然流中的元素是Double类型,但Stream接口没有定义sum方法。

    • 为什么没有呢?比方说你只有一个像Student那样的Stream,把每个学生加起来是没有任何意义的。

    • 但是不要担心,Stream API还提过了原始类型流特化,专门支持处理数值流的方法

    原始类型流特化:

    Java 8引入了三个原始类型流特化接口来解决这个问题,从而不面了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max等…

    • 映射到数值流
      • mapToInt:将Stream类型转化为IntStream类型,执行专属的操作
      • mapToDouble:将Stream类型转化为DoubleStream类型,执行专属的操作
      • mapToLong:将Stream类型转化为LongStream类型,执行专属的操作
    • 转换回对象流:一旦有了数值流,你可能会想把它转换回非特化流
      • boxed:将原始流转换为一般流

    3.1、mapToDouble

    这里只讲解这一个,因为其他两个使用方法和这个一模一样,只是操作的数据类型不一致而已

    比如说:在这里把上面使用reduce进行的求和、最大值、最小值再次优化一遍,并且再获取所有学生的平均分

    • 这里,你会看到OptionalDouble类,这是对于Optional原始类型的特化版本,有OptionalInt、OptionalDouble、OptionalLong三个。因为求和的例子很容易,因为它有一个默认值:0.但是,如果要计算IntStream中的最大元素,就得换个贩子了,因为0是错误的结果。
    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 分数总和
        DoubleStream doubleStream = students.stream()
            .mapToDouble(Student::getScore);
        double sum = doubleStream.sum();
        System.out.println(sum); // 318.0
    
        // 最高分
        OptionalDouble max = students.stream()
            .mapToDouble(Student::getScore)
            .max();
        System.out.println(max); // OptionalDouble[91.0]
    
        // 最低分
        OptionalDouble min = students.stream()
            .mapToDouble(Student::getScore)
            .min();
        System.out.println(min); // OptionalDouble[44.5]
    
        // 平均分
        OptionalDouble average = students.stream()
            .mapToDouble(Student::getScore)
            .average();
        System.out.println(average); // OptionalDouble[63.6]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3.2、boxed

    // 分数总和
    DoubleStream doubleStream = students.stream()
        .mapToDouble(Student::getScore);
    Stream<Double> boxed = doubleStream.boxed();
    
    • 1
    • 2
    • 3
    • 4

    3.3、数值范围

    和数字打交道时,有一个常用的东西就是数值范围。比如,假设你想要生成1和100之间的所有数字。Java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。这两个方法都是第一个参数接收起始值,第二个参数接受结束值。但range是不包含结束值的,而rangeClosed则包含结束值。

    比如说:你想知道1-100的所有偶数的个数

    // 从1-100的所有偶数个数	如果是range则49个
    long count = IntStream.rangeClosed(1, 100)
        .filter(i -> i % 2 == 0)
        .count();
    System.out.println(count); // 50
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4、收集

    • collect(Collector c):将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

    Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)

    另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法如下:

    • List toList:把流中元素收集到List
    • Set toSet:把流中元素收集到Set
    • Collection toCollection:把流中元素收集到创建的集合
    • Long counting:计算流中的元素的个数
    • Integer/Long/Double summingInt/summingLong/summingDouble:对流中元素的整数属性求和
    • Double:averagingInt/averagingLong/averagingDouble:计算流中元素Integer属性的平均值
    • xxxSummaryStatistics summarizingInt/summarizingLong/summarizingDouble:收集流中Integer属性的统计值。如:平均值
    • String joining:连接流中每个字符串
    • Optional maxBy:根据比较器选择最大值
    • Optional minBy:根据比较器选择最小值
    • 归约产生的类型 reducing:从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
    • 转换函数返回的类型 collectingAndThen:包裹另一个收集器,对其结果转换函数
    • Map groupingBy:根据某属性值对流分组,属性为K,结果为V
    • Map partitioningBy:根据true或false进行分区

    4.1、toList

    将流中所有元素都放进List里面,并返回这个List

    比如说:你想获取所有的学生姓名,并放进List里面

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        ArrayList<String> stuName = new ArrayList<>();
        for (Student student : students) {
            stuName.add(student.getName());
        }
        System.out.println(stuName); // 张三、李四、王五、赵六、田七
    
    
        // 1.8写法
        List<String> stuNames = students.stream()
            .map(Student::getName)
            .collect(Collectors.toList());
        System.out.println(stuNames); // 张三、李四、王五、赵六、田七
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4.2、toSet

    将流中所有元素都放进Set里面,并返回这个Set集合。使用这个集合也可以进行去重操作

    比如说:你想获取所有学生的年龄,并且去重(上面写过相同的案例,使用distinct),最后放到Set集合里

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        HashSet<Integer> stuAge = new HashSet<>();
        for (Student student : students) {
            stuName.add(student.getAge());
        }
        System.out.println(stuAge); // 20、21、22
    
    
        // 1.8写法
        Set<Integer> stuAges = students.stream()
            .map(Student::getAge)
            .collect(Collectors.toSet());
        System.out.println(stuAges); // 20、21、22
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4.3、toCollection

    参数为:Supplier collectionFactory类型

    依旧是上面的需求,只不过需要放到LinkedList里面

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        HashSet<Integer> stuAge = new HashSet<>();
        for (Student student : students) {
            stuAge.add(student.getAge());
        }
        LinkedList<Integer> stuAge2 = new LinkedList<>();
        for (Integer integer : stuAge) {
            stuAge2.add(integer);
        }
        System.out.println(stuAge2); // 20、21、22
    
    
        // 1.8写法
        LinkedList<Integer> stuAges = students.stream()
            .map(Student::getAge)
            .distinct()
            .collect(Collectors.toCollection(LinkedList::new));
        System.out.println(stuAges); // 20、21、22
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4.4、counting

    对流中元素进行计数(与上面讲解的count终端操作功能一样)

    比如你需要统计不及格的总共有多少个学生

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        int num = 0;
        for (Student student : students) {
            if (student.getScore() < 60){
                num ++;
            }
        }
        System.out.println(num); // 3
    
    
        // 1.8写法
        Long collects = students.stream()
            .filter(s -> s.getScore() < 60)
            .collect(Collectors.counting());
        System.out.println(collects); // 3
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.5、summingInt/summingLong/summingDouble

    参数为:ToIntFunction类型,对流中所有Integer类型的数据求和

    对数值类型操作,还可以使用上方讲解的特化流进行操作。

    比如说:你想获取所有学生的年龄之和

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        Integer num = 0;
        for (Student student : students) {
            num += student.getAge();
        }
        System.out.println(num); // 104
    
    
        // 1.8写法
        Integer collects = students.stream()
            .collect(Collectors.summingInt(Student::getAge));
        System.out.println(collects); // 104
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4.6、averagingInt/averagingLong/averagingDouble

    对Integer类型的数据求平均值

    比如说:你想知道所有学生年龄和分数的平均值

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        Double num = 0D;
        int count = 0;
        int anum = 0;
        for (Student student : students) {
            count ++;
            num += student.getScore();
            anum += student.getAge();
        }
        // 分数
        double avg = num / count;
        System.out.println(avg); // 63.6
        // 年龄 int / int类型,返回值为int类型的话,会丢失精度
        Double age = Double.valueOf(anum / count);
        System.out.println(age); // 20.8
    
    
        // 1.8写法
        // 分数
        Double avgs = students.stream()
            .collect(Collectors.averagingDouble(Student::getScore));
        System.out.println(avgs); // 104
    
        // 年龄
        Double ages = students.stream()
            .collect(Collectors.averagingInt(Student::getAge));
        System.out.println(ages); // 20.8
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    4.7、summarizingInt/summarizingLong/summarizingDouble

    统计Integer类型的所有统计值,比如:平均值、求和、最大值、最小值、计数,然后把所有结果放到``类型里面

    比如说,你想知道这次考试,共有多少名学生参加了、最高分、最低分、所有学生的分均分,所有学生的总分为多少,这时候,按照上面的写法,你就需要为每个结果创建一个流,然后调用Collectors里面的不同的计算方法。Java为我们提供了一个总的计算方法,可以计算你所有需求的统计

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法,这里就不写了,太多了。。。
    
    
        // 1.8写法
        DoubleSummaryStatistics collect = students.stream()
            .collect(Collectors.summarizingDouble(Student::getScore));
        // DoubleSummaryStatistics{count=5, sum=318.000000, min=44.500000, average=63.600000, max=91.000000}
        System.out.println(collect);
    
        // 最高分
        System.out.println(collect.getMax()); // 91.0
    
        // 最低分
        System.out.println(collect.getMin()); // 44.5
    
        // 平均值
        System.out.println(collect.getAverage()); // 63.6
    
        // 总和
        System.out.println(collect.getSum()); // 318.0
    
        // 总数
        System.out.println(collect.getCount()); // 5
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4.8、joining

    joining方法返回的收集器会把流中每个对象应用toString方法得到的所有字符串连接成一个字符串,内部使用了StringBuilder把生成的字符串逐个追加起来。如果Student类中有一个toString方法来返回学生的名称,则无需映射每个学生的名称就能得到相同的结果。

    • joining():直接拼接,没有拼接符
    • joining(CharSequence delimiter):中间使用指定拼接符进行拼接

    比如说:你想把每个学生名称拼接起来

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        String name = "";
        for (Student student : students) {
            name += student.getName();
        }
        System.out.println(name); // 张三李四王五赵六田七
    
    
        // 1.8写法
        // joining
        String collect = students.stream()
            .map(Student::getName)
            .collect(Collectors.joining());
        System.out.println(collect); // 张三李四王五赵六田七
    
        // joining(CharSequence delimiter)
        String collect2 = students.stream()
            .map(Student::getName)
            .collect(Collectors.joining(","));
        System.out.println(collect2); // 张三,李四,王五,赵六,田七
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    4.9、maxBy

    根据自定义排序规则,进行定制排序,获取最大值

    比如说:你想获取最高分学生的信息

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        Comparator<Student> comparator = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Student s1 = (Student)o1;
                Student s2 = (Student)o2;
                return -s1.getScore().compareTo(s2.getScore());
            }
        };
        students.sort(comparator);
        System.out.println(students.get(0)); // Student{name='田七', age=21, clazz='103班', score=91.0}
    
    
        // 1.8写法
        // joining
        Optional<Student> collect = students.stream()
            .collect(Collectors.maxBy(Comparator.comparingDouble(Student::getScore)));
        System.out.println(collect); // Optional[Student{name='田七', age=21, clazz='103班', score=91.0}]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4.10、minBy

    根据自定义排序规则,进行定制排序,获取最小值

    比如说:你想获取最低分学生的信息

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        Comparator<Student> comparator = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Student s1 = (Student)o1;
                Student s2 = (Student)o2;
                return s1.getScore().compareTo(s2.getScore());
            }
        };
        students.sort(comparator);
        System.out.println(students.get(0)); // Student{name='王五', age=22, clazz='102班', score=44.5}
    
    
        // 1.8写法
        // joining
        Optional<Student> collect = students.stream()
            .collect(Collectors.minBy(Comparator.comparingDouble(Student::getScore)));
        System.out.println(collect); // Optional[Student{name='王五', age=22, clazz='102班', score=44.5}]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4.11、reducing

    有三个重载方法

    • Collector reducing(T identity, BinaryOperator op):
    • Collector reducing(BinaryOperator op):
    • Collector reducing(U identity, Function mapper,BinaryOperator op)
      • 第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值和而言,0是一个合适的值
      • 第二个参数相当于是map方法,将Student类中的用于计算的数值提取出来
      • 第三个参数是一个BinaryOperator,将两个项目累计成一个同类型的值。这里就是对Double类型进行求和

    跟上面讲的reduce归约基本一样,只有参数有差异

    比如说:你想获取所有学生的总分

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
    
    
        // 1.8写法
        Double collect1 = students.stream()
            .collect(Collectors.reducing(0D, Student::getScore, Double::sum));
        System.out.println(collect1); // 318.0
    
        Optional<Double> collect2 = students.stream()
            .map(Student::getScore)
            .collect(Collectors.reducing(Double::sum));
        System.out.println(collect2); // Optional[318.0]
    
        Double aDouble = new Double(82D);
        Double collect3 = students.stream()
            .map(Student::getScore)
            .collect(Collectors.reducing(aDouble, Double::sum));
        System.out.println(collect3); // 400.0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4.12、collectingAndThen

    包裹另一个收集器,对其结果转换函数

    比如说,你想使用上面第二种写法,但是又不想自己单独拆掉Optional的包装,可以使用如下方法

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
    
    
        // 1.8写法
        Double collect = students.stream()
            .map(Student::getScore)
            .collect(Collectors.collectingAndThen(Collectors.reducing(Double::sum), Optional::get));
        System.out.println(collect); // 318.0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.13、groupingBy

    分组(重要)

    根据某属性值对流分组,属性为K结果为V

    • groupingBy(Function classifier):根据指定数据进行分组
    • groupingBy(Function classifier, Collector downstream):分好组后,可以对分完组的List进行其他操作,可以通过该方法,对分好组的集合进行统计、取最大值、最小值等操作
    • groupingBy(Function classifier, Supplier mapFactory, Collector downstream):不晓得,很少用。。。
    • 可以进行单级分组,
    • 也可以进行多级分组
    单级分组

    groupingBy(Function classifier)

    比如说,你想按照班级对各个班的学生进行分组

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        HashMap<String, List<Student>> map = new HashMap<>();
        ArrayList<Student> list = new ArrayList<>();
        for (Student student : students) {
            String clazz = student.getClazz();
            List<Student> l = map.get(clazz);
            // 如果分组不存在,则创建一个新分组
            if (l == null){
                ArrayList<Student> lis = new ArrayList<>();
                lis.add(student);
                map.put(clazz,lis);
                continue;
            }
            l.add(student);
        }
        /*
                key:100班
                value:[Student{name='张三', age=20, clazz='100班', score=88.0}]
                key:101班
                value:[Student{name='李四', age=21, clazz='101班', score=50.0}]
                key:102班
                value:[Student{name='王五', age=22, clazz='102班', score=44.5}]
                key:103班
                value:[Student{name='赵六', age=20, clazz='103班', score=44.5}, Student{name='田七', age=21, clazz='103班', score=91.0}]
             */
        Set<Map.Entry<String, List<Student>>> entries = map.entrySet();
        for (Map.Entry<String, List<Student>> entry : entries) {
            System.out.println("key:"+ entry.getKey());
            System.out.println("value:"+ entry.getValue());
        }
    
        System.out.println("******************************************");
    
        // 1.8写法
        Map<String, List<Student>> collect = students.stream()
            .collect(Collectors.groupingBy(Student::getClazz));
    
        Set<Map.Entry<String, List<Student>>> entries2 = collect.entrySet();
        /*
                key:100班
                value:[Student{name='张三', age=20, clazz='100班', score=88.0}]
                key:101班
                value:[Student{name='李四', age=21, clazz='101班', score=50.0}]
                key:102班
                value:[Student{name='王五', age=22, clazz='102班', score=44.5}]
                key:103班
                value:[Student{name='赵六', age=20, clazz='103班', score=44.5}, Student{name='田七', age=21, clazz='103班', score=91.0}]
             */
        for (Map.Entry<String, List<Student>> entry : entries2) {
            System.out.println("key:"+ entry.getKey());
            System.out.println("value:"+ entry.getValue());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    groupingBy(Function classifier, Collector downstream)

    比如说:你想统计每个班的人数

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "101班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D)
        );
    
        // 1.7及之前写法
        HashMap<String, List<Student>> map = new HashMap<>();
        ArrayList<Student> list = new ArrayList<>();
        for (Student student : students) {
            String clazz = student.getClazz();
            List<Student> l = map.get(clazz);
            // 如果分组不存在,则创建一个新分组
            if (l == null){
                ArrayList<Student> lis = new ArrayList<>();
                lis.add(student);
                map.put(clazz,lis);
                continue;
            }
            l.add(student);
        }
        Set<Map.Entry<String, List<Student>>> entries = map.entrySet();
        HashMap<String, Integer> map2 = new HashMap<>();
        for (Map.Entry<String, List<Student>> entry : entries) {
            map2.put(entry.getKey(),entry.getValue().size());
        }
        System.out.println(map2); // {100班=1, 101班=1, 102班=1, 103班=2}
    
    
        System.out.println("******************************************");
    
        // 1.8写法
        Map<String, Long> collect = students.stream()
            .collect(Collectors.groupingBy(Student::getClazz, Collectors.counting()));
    
        System.out.println(collect); // {100班=1, 101班=1, 102班=1, 103班=2}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    多级分组

    groupingBy(Function classifier, Collector downstream)

    groupBy方法里面可以嵌套groupBy,进行多级分组

    比如说:你想对班级分完组后,对合格和不合格的学生分下组···

    public static void main(String[] args){
            // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D),
            new Student("李四", 21, "100班", 50D),
            new Student("王五", 22, "102班", 44.5D),
            new Student("赵六", 20, "103班", 44.5D),
            new Student("田七", 21, "103班", 91D),
            new Student("小八", 21, "103班", 61D)
        );
    
        // 1.7及之前写法(如果不想了解的可以直接看下面1.8写法)
        HashMap<String, Map<String,List<Student>>> clazzMap = new HashMap<>();
        for (Student student : students) {
            Map<String, List<Student>> clazzMap2 = clazzMap.get(student.getClazz());
            // 判断外部map是否分组
            if(clazzMap2 == null){
                HashMap<String, List<Student>> cMap = new HashMap<>();
                clazzMap.put(student.getClazz(),cMap);
            }
            // 记录当前学生成绩状态
            String isHeGe = "";
            if(student.getScore() > 60){
                isHeGe = "及格";
            }else {
                isHeGe = "不及格";
            }
    
            // 重新获取分组
            clazzMap2 = clazzMap.get(student.getClazz());
            // 判断内部map是否分组
            List<Student> isPassList = clazzMap2.get(isHeGe);
            if (isPassList == null){
                ArrayList<Student> sList = new ArrayList<>();
                sList.add(student);
                clazzMap2.put(isHeGe,sList);
                continue;
            }
            isPassList.add(student);
        }
        /*
                班级:100班
                不及格:[Student{name='李四', age=21, clazz='100班', score=50.0}]
                及格:[Student{name='张三', age=20, clazz='100班', score=88.0}]
                --------------------
                班级:102班
                不及格:[Student{name='王五', age=22, clazz='102班', score=44.5}]
                --------------------
                班级:103班
                不及格:[Student{name='赵六', age=20, clazz='103班', score=44.5}]
                及格:[Student{name='田七', age=21, clazz='103班', score=91.0}, Student{name='小八', age=21, clazz='103班', score=61.0}]
             */
        Set<Map.Entry<String, Map<String, List<Student>>>> entrySet = clazzMap.entrySet();
        entrySet.forEach(m -> {
            System.out.println("--------------------");
            System.out.println("班级:" + m.getKey());
            Map<String, List<Student>> value = m.getValue();
            Set<Map.Entry<String, List<Student>>> entries = value.entrySet();
            entries.forEach(map -> {
                System.out.println(map.getKey() + ":" + map.getValue());
            });
        });
    
    
        System.out.println("******************************************");
    
        // 1.8写法
        Map<String, Map<String, List<Student>>> collect = students.stream()
            .collect(Collectors.groupingBy(Student::getClazz, Collectors.groupingBy(s -> {
                if (s.getScore() >= 60) {
                    return "及格";
                } else {
                    return "不及格";
                }
            })));
    
        Set<Map.Entry<String, Map<String, List<Student>>>> entries2 = collect.entrySet();
    
        /*
                班级:100班
                不及格:[Student{name='李四', age=21, clazz='100班', score=50.0}]
                及格:[Student{name='张三', age=20, clazz='100班', score=88.0}]
                --------------------
                班级:102班
                不及格:[Student{name='王五', age=22, clazz='102班', score=44.5}]
                --------------------
                班级:103班
                不及格:[Student{name='赵六', age=20, clazz='103班', score=44.5}]
                及格:[Student{name='田七', age=21, clazz='103班', score=91.0}, Student{name='小八', age=21, clazz='103班', score=61.0}]
    
             */
        entries2.forEach(m -> {
            System.out.println("--------------------");
            System.out.println("班级:" + m.getKey());
            Map<String, List<Student>> value = m.getValue();
            Set<Map.Entry<String, List<Student>>> entries = value.entrySet();
            entries.forEach(map -> {
                System.out.println(map.getKey() + ":" + map.getValue());
            });
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    4.14、partitioningBy

    分区

    分区的好处在于保留了分区函数返回truefalse的两套流元素列表,分区的依据一定要是布尔类型

    • partitioningBy(Predicate predicate):按照指定的条件分组
    • partitioningBy(Predicate predicate, Collector downstream):可以对分好的组再次进行操作,可以通过该方法,对分好组的集合进行统计、取最大值、最小值等操作

    这里我们在Student类里面新加一个属性,并重新生成有参构造和getter/setter方法

    // 是否在校
    private Boolean isAtSchool;
    
    • 1
    • 2
    单级分区

    partitioningBy(Predicate predicate)

    比如说:你想根据学生是否在校分成两个区域,一个在校的所有学生,一个不在校的学生

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D,true),
            new Student("李四", 21, "100班", 50D,true),
            new Student("王五", 22, "102班", 44.5D,false),
            new Student("赵六", 20, "103班", 44.5D,true),
            new Student("田七", 21, "103班", 91D,false),
            new Student("小八", 21, "103班", 61D,false)
        );
    
        // 1.7及之前写法
        HashMap<Boolean, List<Student>> map = new HashMap<>();
        for (Student student : students) {
            Boolean atSchool = false;
            // 先获取学生是否在校
            if (student.getAtSchool()){
                atSchool = true;
            }
            List<Student> list = map.get(atSchool);
            // 如果没有创建过分组
            if(list == null){
                ArrayList<Student> l = new ArrayList<>();
                l.add(student);
                map.put(atSchool,l);
                continue;
            }
            // 创建过分组
            list.add(student);
        }
        Set<Map.Entry<Boolean, List<Student>>> entries1 = map.entrySet();
        /*
                key:false
                value:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}, 
                        Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false}, 
                        Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]
                key:true
                value:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}, 
                        Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true}, 
                        Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]
    
             */
        entries1.forEach(entry -> {
            System.out.println("key:" + entry.getKey());
            System.out.println("value:" + entry.getValue());
        });
    
        System.out.println("******************************************");
    
        // 1.8写法
        Map<Boolean, List<Student>> collect = students.stream()
            .collect(Collectors.partitioningBy(Student::getAtSchool));
    
        Set<Map.Entry<Boolean, List<Student>>> entries = collect.entrySet();
        /*
                key:false
                value:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false},
                        Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false},
                        Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]
                key:true
                value:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true},
                        Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true},
                        Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]
    
             */
        entries.forEach(entry -> {
            System.out.println("key:" + entry.getKey());
            System.out.println("value:" + entry.getValue());
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    partitioningBy(Predicate predicate, Collector downstream)

    比如说:你想知道在校生和不在校生的人数

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D,true),
            new Student("李四", 21, "100班", 50D,true),
            new Student("王五", 22, "102班", 44.5D,false),
            new Student("赵六", 20, "103班", 44.5D,true),
            new Student("田七", 21, "103班", 91D,false),
            new Student("小八", 21, "103班", 61D,false)
        );
    
        // 1.7及之前写法
        HashMap<Boolean, List<Student>> map = new HashMap<>();
        for (Student student : students) {
            // 先获取学生是否在校
            Boolean atSchool = student.getAtSchool();
            
            // 尝试获取分组
            List<Student> list = map.get(atSchool);
            // 如果没有创建过分组
            if(list == null){
                ArrayList<Student> l = new ArrayList<>();
                l.add(student);
                map.put(atSchool,l);
                continue;
            }
            // 创建过分组
            list.add(student);
        }
        Set<Map.Entry<Boolean, List<Student>>> entries1 = map.entrySet();
        entries1.forEach(entry -> {
            /*
                    false=3
                    true=3
                 */
            System.out.println(entry.getKey() + "=" + entry.getValue().size());
        });
    
        System.out.println("******************************************");
    
        // 1.8写法
        Map<Boolean, Long> collect = students.stream()
            .collect(Collectors.partitioningBy(Student::getAtSchool, Collectors.counting()));
        System.out.println(collect); // {false=3, true=3}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    多级分区

    partitioningBy(Predicate predicate, Collector downstream)

    比如说:你想对是否在校的学生分完区后,再根据成绩是否合格进行分区显示

    public static void main(String[] args){
        // 元数据
        List<Student> students = Arrays.asList(
            new Student("张三", 20, "100班", 88D,true),
            new Student("李四", 21, "100班", 50D,true),
            new Student("王五", 22, "102班", 44.5D,false),
            new Student("赵六", 20, "103班", 44.5D,true),
            new Student("田七", 21, "103班", 91D,false),
            new Student("小八", 21, "103班", 61D,false)
        );
    
        // 1.7及之前写法
        HashMap<Boolean, Map<Boolean,List<Student>>> map = new HashMap<>();
        for (Student student : students) {
            Boolean atSchool = student.getAtSchool();
    
            Map<Boolean, List<Student>> atSchoolGroupMap = map.get(atSchool);
            // 如果没有根据是否在校分过组
            if (atSchoolGroupMap == null){
                // 创建一个空的内map分组(根据是否合格分组)
                HashMap<Boolean, List<Student>> map1 = new HashMap<>();
                map.put(atSchool,map1);
            }
    
            // 获取当前学生是否合格
            Boolean isHeGe = false;
            if (student.getScore() > 60){
                isHeGe = true;
            }
    
            // 重新获取map
            Map<Boolean, List<Student>> atSchoolGroupMap2 = map.get(atSchool);
            List<Student> stu = atSchoolGroupMap2.get(isHeGe);
            // 如果为null,说明没有创建学生是否合格的分组
            // 就创建一个集合,把当前新建的分组添加到内map中
            if (stu == null) {
                ArrayList<Student> list = new ArrayList<>();
                list.add(student);
                atSchoolGroupMap2.put(isHeGe,list);
                // 当前数据处理完毕,处理下一条
                continue;
            }
    
            // 如果有集合了,就直接添加进去即可
            stu.add(student);
        }
        Set<Map.Entry<Boolean, Map<Boolean, List<Student>>>> entries1 = map.entrySet();
        /*
                key:false
                成绩是否合格:false
                结果:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}]
                成绩是否合格:true
                结果:[Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false}, 
                    Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]
                ----------------------------------------
                key:true
                成绩是否合格:false
                结果:[Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true}, 
                    Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]
                成绩是否合格:true
                结果:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}]
             */
        entries1.forEach(entrie -> {
            System.out.println("----------------------------------------");
            System.out.println("key:" + entrie.getKey());
            Map<Boolean, List<Student>> value = entrie.getValue();
            Set<Map.Entry<Boolean, List<Student>>> val = value.entrySet();
            val.forEach( v -> {
                System.out.println("成绩是否合格:" + v.getKey());
                System.out.println("结果:" + v.getValue());
            });
        });
    
        System.out.println("***************版本分界线**************************");
    
        // 1.8写法
        Map<Boolean, Map<Boolean, List<Student>>> collect = students.stream()
            .collect(Collectors.partitioningBy(Student::getAtSchool, Collectors.partitioningBy(s -> {
                if (s.getScore() > 60) {
                    return true;
                }
                return false;
            })));
        Set<Map.Entry<Boolean, Map<Boolean, List<Student>>>> entries = collect.entrySet();
        /*
                key2:false
                成绩是否合格:false
                结果:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}]
                成绩是否合格:true
                结果:[Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false},
                    Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]
                ----------------------------------------
                key:true
                成绩是否合格:false
                    结果:[Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true},
                        Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]
                成绩是否合格:true
                    结果:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}]
             */
        entries.forEach(entrie -> {
            System.out.println("----------------------------------------");
            System.out.println("key:" + entrie.getKey());
            Map<Boolean, List<Student>> value = entrie.getValue();
            Set<Map.Entry<Boolean, List<Student>>> val = value.entrySet();
            val.forEach( v -> {
                System.out.println("成绩是否合格" + v.getKey());
                System.out.println("结果" + v.getValue());
            });
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
  • 相关阅读:
    Kubernetes 可观测性:利用 4 个开源工具
    US1MF-ASEMI贴片快恢复二极管US1MF
    Spring Boot 2.x系列【10】功能篇之使用 ApplicationRunner 、CommandLineRunner
    Redis:Feed流之Timeline的实现
    AUTOSAR汽车电子嵌入式编程精讲300篇-基于“可自动配置的底层软件” 的开放式汽车电子软件架构研究(中)
    Mybatis核心源码赏析(三)
    【FPGA教程案例87】加解密1——基于FPGA的AES加解密算法verilog实现
    get请求接收JSON格式数据
    Java Class isAnonymousClass()实例讲解
    floyd-warshall算法浅理解
  • 原文地址:https://blog.csdn.net/qq_57404736/article/details/128154664