• Stream API


    Stream API执行流程

    Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,可以极大地提高程序员生产力,让程序员写出高效、简洁的代码

    实际开发中项目中多数数据源都是来自MySQL、Oracle等关系型数据库,还有部分来自MongDB、Redis等非关系型数据库

    • 当我们从关系型数据库中查询数据时,可以先使用SQL语句在数据库中就对数据进行过滤,排序等操作,最后后台Java程序接收
    • 当我们从非关系型数据库中查询数据时,由于不能在数据库中使用SQL语句操作数据,此时只能靠后台Java程序(Stream API)操作数据库中查询到的数据

    Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合/数组进行的操作(可是是非常复杂的查找、过滤和映射数据等操作)

    • 使用Stream API对内存数据(集合/数组)进行操作就类似于使用SQL语句对数据库表中数据的操作,即Stream API提供了一种高效且易于使用的处理数据的方式

    Stream和Collection集合的区别: Stream关注的是数据的运算(过滤操作)与CPU打交道, 集合关注的是数据的存储(静态的存储结构)与内存打交道

    Stream的特性

    • Stream自己不会存储元素
    • Stream不会改变源对象,相反他们会返回一个持有结果的新Stream
    • Stream操作是延迟执行的,只有执行了终止操作后才会执行中间操作链并产生结果
    • Stream一旦执行了终止操作,就不能再次使用该Stream执行其它中间操作或终止操作了,得重新创建一个新的流才行

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    数据准备

    public class Employee {
    
        private int id;
        private String name;
        private int age;
        private double salary;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public double getSalary() {
            return salary;
        }
    
        public void setSalary(double salary) {
            this.salary = salary;
        }
    
        public Employee() {
            System.out.println("Employee().....");
        }
    
        public Employee(int id) {
            this.id = id;
            System.out.println("Employee(int id).....");
        }
    
        public Employee(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Employee(int id, String name, int age, double salary) {
    
            this.id = id;
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
    
            Employee employee = (Employee) o;
    
            if (id != employee.id)
                return false;
            if (age != employee.age)
                return false;
            if (Double.compare(employee.salary, salary) != 0)
                return false;
            return name != null ? name.equals(employee.name) : employee.name == null;
        }
    
        @Override
        public int hashCode() {
            int result;
            long temp;
            result = id;
            result = 31 * result + (name != null ? name.hashCode() : 0);
            result = 31 * result + age;
            temp = Double.doubleToLongBits(salary);
            result = 31 * result + (int) (temp ^ (temp >>> 32));
            return result;
        }
    }
    
    • 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

    提供用于测试数据的类

    public class EmployeeData {
        public static List<Employee> getEmployees() {
            List<Employee> list = new ArrayList<>();
            list.add(new Employee(1001, "马化腾", 34, 6000.38));
            list.add(new Employee(1002, "马云", 12, 9876.12));
            list.add(new Employee(1003, "刘强东", 33, 3000.82));
            list.add(new Employee(1004, "雷军", 26, 7657.37));
            list.add(new Employee(1005, "李彦宏", 65, 5555.32));
            list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
            list.add(new Employee(1007, "任正非", 26, 4333.32));
            list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
    
            return list;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    通过一个数据源创建Stream

    方式一通过集合:Java8中的Collection接口扩展了两个获取Stream流的方法

    方法名功能
    default Stream stream()返回一个顺序流(按照顺序操作元素)
    default Stream parallelStream()返回一个并行流(并发的操作元素)
    @Test
    public void test01(){
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        // 返回一个顺序流
        Stream<Integer> stream1 = list.stream();
        // 返回一个并行流
        Stream<Integer> stream2 = list.parallelStream();
        
        List<Employee> employees = EmployeeData.getEmployees();
        // 返回一个顺序流
        Stream<Employee> stream = employees.stream();
        // 返回一个并行流
        Stream<Employee> employeeStream = employees.parallelStream();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    方式二通过数组: Java8中的Arrays的静态方法stream()可以获取对应类型的Stream流

    方法名功能
    static Stream stream(T[] array)返回一个自定义类型的Stream流
    public static IntStream stream(int[] array)返回一个int类型的Stream流
    public static LongStream stream(long[] array)返回一个long类型的Stream流
    public static DoubleStream stream(double[] array)返回一个double类型的Stream流
    @Test
    public void test(){
        // 获取基本数据类型的Stream流
        int[] arr = {1,2,3,4,5};
        IntStream stream = Arrays.stream(arr);
        
        // 获取引用数据类型的Stram流
        String[] arr = {"hello","world"};
        Stream<String> stream = Arrays.stream(arr); 
        
        // 获取自定义类型的Stream流
        Employee kyle = new Employee(9527, "Kyle");
        Employee lucy = new Employee(9421, "Lucy");
        Employee[] employees = {kyle, lucy};
        Stream<Employee> stream1 = Arrays.stream(employees);
        
        
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    方式三通过指定具体数据: 调用Stream类静态方法of()创建一个流

    方法名功能
    public static Stream of(T… values)通过手动指定任意个数据创建一个流
    @Test
    public void test04(){
        Stream<Integer> stream = Stream.of(1,2,3,4,5);
        stream.forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    方式四调用Stream类的静态方法iterate()和generate()创建无限流(了解)

    方法名功能
    public static Stream iterate(final T seed, final UnaryOperator f)迭代
    public static Stream generate(Supplier s)生成
    @Test
    public void test() {
        // 从0开始迭代遍历10个数,没有limit限制会无限迭代
        Stream.iterate(0, t -> t + 1).limit(10).forEach(System.out::println);
    
        // 生成10个随机数,没有limit限制会无限生成
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    中间操作筛选与切片

    中间操作就是处理数据的具体操作,每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象

    • 中间操作可以是个操作链可对数据源的数据进行n次处理(多个中间操作),但是所有的中间操作只有在执行终止操作时才会一次性全部处理(惰性求值)
    • Stream流一旦执行了终止操作,就不能再次使用该Stream执行其它中间操作,需要根据数据源重新创建一个新的Stream流
    方 法描 述
    filter(Predicatep)接收Lambda表达式然后从流中排除某些元素
    distinct()筛选通过流所生成元素的 hashCode() 和 equals() 去除重复元素
    limit(long maxSize)截断流使其元素不超过给定数量
    skip(long n)跳过元素返回一个扔掉了前 n个元素的流
    若流中元素不足n个则返回一个空流,与 limit(n) 互补
    @Test
    public void test() {
        List<Employee> employees = EmployeeData.getEmployees();
        //1. 查询工资大于7000的员工信息
        employees.stream().filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);
        System.out.println("----------------------------");
        //2. 只输出3条员工信息
        employees.stream().limit(3).forEach(System.out::println);
        System.out.println("----------------------------");
        //3. 跳过前3个元素
        employees.stream().skip(3).forEach(System.out::println);
        System.out.println("----------------------------");
        //4. 通过流所生成元素的hashCode和equals方法去除重复元素
        employees.add(new Employee(9527, "Kyle", 20, 9999));
        employees.add(new Employee(9527, "Kyle", 20, 9999));
        employees.stream().distinct().forEach(System.out::println);
    }
    /*
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    ----------------------------
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1003, name='刘强东', age=33, salary=3000.82}
    ----------------------------
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    Employee{id=1007, name='任正非', age=26, salary=4333.32}
    Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
    ----------------------------
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1003, name='刘强东', age=33, salary=3000.82}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    Employee{id=1007, name='任正非', age=26, salary=4333.32}
    Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
    Employee{id=9527, name='Kyle', age=20, salary=9999.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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    中间操作映射新的Stream

    方法描述
    map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上并将其映射成一个新的元素(将元素转换成其他形式或提取信息)
    mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 DoubleStream
    mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 IntStream
    mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 LongStream
    flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流然后把所有流连接成一个流,

    map和flatMap的区别: map方法最终会得到一个含有多个Stream实例的Stream,flatMap最终会得到一个含有多个元素的Stream

    @Test
    public void test() {
        ArrayList list1 = new ArrayList();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        
        ArrayList list2 = new ArrayList();
        list2.add(1);
        list2.add(2);
        list2.add(3);
    	// 集合中有4个元素,[1,2,3,[4,5,6]],类似map
        list1.add(list2);
        // 集合中有6个元素,[1,2,3,4,5,6],类似flatMap
        list1.addAll(list2);
        System.out.println(list1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    public class LambdaTest{
        @Test
        public void test() {
            List<String> strings = Arrays.asList("aa", "bb", "cc", "dd");
            List<Employee> employees = EmployeeData.getEmployees();
            // 将集合所有元素转为大写并输出
            strings.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
            System.out.println("--------------------------");
            // 获取员工姓名长度大于3的员工的姓名
            employees.stream().map(Employee::getName).
                // 过滤出name长度>3的员工
                filter(name -> name.length() > 3).
                // 遍历输出
                forEach(System.out::println);
            System.out.println("--------------------------");
            // 使用map将字符串中的多个字符构成的集合转换为对应的Stream实例,然后再获取每一个Stream中的元素
            strings.stream().map(LambdaTest::formStringToStream).
                forEach(characterStream -> characterStream.forEach(System.out::println));
            // 使用flatMap可以直接获取Stream中Stream实例中的元素
            System.out.println("--------------------------");
            strings.stream().flatMap(LambdaTest::formStringToStream).forEach(System.out::println);
        }
    
        public static Stream<Character> formStringToStream(String str) {
            ArrayList<Character> list = new ArrayList<>();
            for (char c : str.toCharArray()) {
                list.add(c);
            }
            return list.stream();
        }
    
    }
    /*
    AA
    BB
    CC
    DD
    --------------------------
    比尔盖茨
    扎克伯格
    --------------------------
    a
    a
    b
    b
    c
    c
    d
    d
    --------------------------
    */
    
    • 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

    中间操作排序

    方法描述
    sorted()产生一个新流其中按自然顺序排序
    sorted(Comparator com)产生一个新流其中按比较器顺序排序
    @Test
    public void test20() {
        List<Integer> nums = Arrays.asList(13, 54, 97, 52, 43, 64, 27);
        List<Employee> employees = EmployeeData.getEmployees();
        //自然排序,如果要对employees自然排序就需要Employee类实现Comparable接口
        nums.stream().sorted().forEach(System.out::println);
        //定制排序,先按照年龄升序排,再按照工资降序排
        employees.stream().sorted((o1, o2) -> {
            int compare = Integer.compare(o1.getAge(), o2.getAge());
            if (compare != 0) {
                return compare;
            } else {
                return -Double.compare(o1.getSalary(), o2.getSalary());
            } 
        }).forEach(System.out::println);
    }
    
    /*
    13
    27
    43
    52
    54
    64
    97
    ------------------
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1007, name='任正非', age=26, salary=4333.32}
    Employee{id=1003, name='刘强东', age=33, salary=3000.82}
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
    */
    
    • 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

    终止操作匹配与查找

    终端操作会从流的流水线生成结果,方法返回值类型就不再是Stream了可以是任何不是流的值(如List,Integer,void等)

    • 流进行了终止操作后不能再次使用执行其他中间操作或终止操作
    方法描述
    allMatch(Predicate p)检查是否匹配所有元素
    anyMatch(Predicate p)检查是否至少匹配一个元素
    noneMatch(Predicate p)检查是否没有匹配所有元素
    findFirst()返回第一个元素
    findAny()返回当前流中的任意元素,顺序流会取第一个元素
    count()返回流中元素总数
    max(Comparator c)返回流中最大值
    min(Comparator c)返回流中最小值
    forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
    相反,Stream API 使用内部迭代——它帮你把迭代做了)
    @Test
    public void test21(){
        List<Employee> employees = EmployeeData.getEmployees();
        // 练习:是否所有的员工的工资是否都大于5000
        System.out.println("是否所有的员工的工资是否都大于5000:"+employees.stream().allMatch(employee -> employee.getSalary() > 5000));
        // 练习:是否存在员工年龄小于15
        System.out.println("是否存在员工年龄小于15:"+employees.stream().anyMatch(employee -> employee.getAge() < 15));
        // 练习:是否不存在员工姓“马”
        System.out.println("是否不存在员工姓马:"+employees.stream().noneMatch(employee -> employee.getName().startsWith("马")));
        // 返回流中的第一个元素
        System.out.println("返回第一个元素:"+employees.stream().findFirst());
        // 返回当前流中的任意元素
        System.out.println("返回当前流中的任意元素"+employees.stream().findAny());
        // 返回流中元素的总个数(返回总个数前可以先过滤)
        System.out.println("返回元素总数:"+employees.stream().count());
        // 返回流中最高工资
        System.out.println("返回最高工资:"+employees.stream().map(Employee::getSalary).max(Double::compare));
        // 返回最低工资的员工
        System.out.println("返回最高工资:"+employees.stream().min(e1,e2 -> Double.compare(e1.getSalary(),e2.getSalary()));
        // 返回流中最小值
        System.out.println("返回最小年龄:"+employees.stream().map(Employee::getAge).min(Integer::compare));
        // 内部迭代
        employees.stream().forEach(System.out::println);
        System.out.println("-------------");
        // 使用集合的遍历操作
        employees.forEach(System.out::println);
    }
    
    /*
    是否所有的员工的工资是否都大于5000:false
    是否存在员工年龄小于15:true
    是否不存在员工姓马:false
    返回第一个元素:Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
    返回当前流中的任意元素Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
    返回元素总数:8
    返回最高工资:Optional[9876.12]
    返回最小年龄:Optional[12]
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1003, name='刘强东', age=33, salary=3000.82}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    Employee{id=1007, name='任正非', age=26, salary=4333.32}
    Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
    -------------
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1003, name='刘强东', age=33, salary=3000.82}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    Employee{id=1007, name='任正非', age=26, salary=4333.32}
    Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
    */    
    
    • 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

    终止操作归约

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

    方法描述
    reduce(T identity, BinaryOperator b)可以将流中元素反复结合起来返回一个值T
    reduce(BinaryOperator b)可以将流中元素反复结合起来返回一个值Optional
    @Test
    public void test22() {
        List<Integer> nums = Arrays.asList(13, 32, 23, 31, 94, 20, 77, 21, 17);
        List<Employee> employees = EmployeeData.getEmployees();
        // 练习1:计算1-10的自然数的和(0是初始值)
        System.out.println(nums.stream().reduce(0, Integer::sum));
        // 练习2:手动计算公司所有员工工资总和
        System.out.println(employees.stream().map(Employee::getSalary).reduce((o1, o2) -> o1 + o2));
        // 调用Integer的sum方法计算年龄总和
        System.out.println(employees.stream().map(Employee::getAge).reduce(Integer::sum));
        // 计算公司所有员工工资综合
        Stream<Double> salaryStream = employ.stream.map(Employee::getSalary);
        Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
        System.out.println(sumMoney);
    }
    /*
    328
    Optional[48424.08]
    Optional[273]
    Optional[48424.08]
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    终止操作收集

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

    Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map),可以方便地创建常见收集器实例

    方法返回类型作用
    toListList把流中元素收集到List
    List emps= list.stream().collect(Collectors.toList());
    toSetList把流中元素收集到List
    Set emps= list.stream().collect(Collectors.toSet());
    toCollectionCollection把流中元素收集到创建的集合
    Collection emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
    countingLong计算流中元素的个数
    long count = list.stream().collect(Collectors.counting());
    summinglntInteger对流中元素的整数属性求和
    int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
    averagingIntDouble计算流中元素Integer属性的平均值
    double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
    summarizinglntIntSummaryStatistics收集流中Integer属性的统计值。如:平均值
    int SummaryStatisticsiss=list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
    joiningString连接流中每个字符串
    String str= list.stream().map(Employee::getName).collect(Collectors.joining());
    maxByOptional根据比较器选择最大值
    optionalmax=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary));
    minByOptional根据比较器选择最小值
    Optionalmin = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary));
    reducing归约产生的类型从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
    int total=list.stream().collect(Collectors.reducing(0, Employe::getSalar, Integer::sum));
    collectingAndThen转换函数返回的类型包裹另一个收集器,对其结果转换函数
    int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
    groupingByMap根据某属性值对流分组,属性为K,结果为V
    Mapmap= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
    partitioningByMap根据true或false进行分区
    Mapvd=list.stream().collect(Collectors.partitioningBy(Employee::getManage));
    @Test
    public void test23() {
        // 练习1:查找工资大于6000的员工,结果返回为一个List
        List<Employee> employees = EmployeeData.getEmployees();
        List<Employee> list = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("--------------------");
        // 练习2:查找年龄大于20的员工,结果返回为一个List
        employees.add(new Employee(9527,"Kyle",21,9999));
        employees.add(new Employee(9527,"Kyle",21,9999));
        Set<Employee> set = employees.stream().filter(employee -> employee.getAge() > 20).collect(Collectors.toSet());
        set.forEach(System.out::println);
    }
    
    /*
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1002, name='马云', age=12, salary=9876.12}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    --------------------
    Employee{id=1001, name='马化腾', age=34, salary=6000.38}
    Employee{id=1007, name='任正非', age=26, salary=4333.32}
    Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
    Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
    Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
    Employee{id=1003, name='刘强东', age=33, salary=3000.82}
    Employee{id=9527, name='Kyle', age=21, salary=9999.0}
    Employee{id=1004, name='雷军', age=26, salary=7657.37}
    */
    
    • 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

    Optional类

    空指针异常是导致Java应用程序失败的最常见原因,以前为了解决空指针异常Google公司著名的Guava项目引入了Optional类(通过检查空值的方式来防止代码污染)

    受到Google Guava的启发Java 8也引入了java.util.Optional类,Optional类是一个可以为null的容器对象

    • Optional类可以保存类型T的值代表这个值存在
    • 以前用null表示一个值不存在,现在Optional类保存null表示这个值不存在可以避免空指针异常

    创建Optional类

    创建Optional类对象的方法

    方法名功能
    Optional.of(T t)创建一个Optional实例,t必须非空
    Optional.empty()创建一个空的Optional实例
    Optional.ofNullable(T t)t可以为null

    准备实体类Boy和Girl

    public class Boy {
        private Girl girl;
        public Boy() {
        }
    
        public Boy(Girl girl) {
            this.girl = girl;
        }
        @Override
        public String toString() {
            return "Boy{" +
                    "girl=" + girl +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    public class Girl {
        private String name;
        public Girl() {
        }
    
        public Girl(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Girl{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    @Test
    public void test(){
        Girl girl = new Girl();
        // 如果girl等于null会报空指针异常
        Optional<Girl> optionalGirl = Optional.of(girl);
    }
    
    @Test
    public void test25(){
        Girl girl = new Girl();
        girl = null;
        // girl可以为null
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        // girl为null输出Optional.empty,girl不为null输出Optional[Girl{name="null"}]
        System.out.println(optionalGirl);
        
        // girl为null输出Girl{name="赵丽颖"},girl不为null输出Girl{name="null"}
        Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
        System.out.println(girl1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    使用Optional

    判断Optional容器中是否包含对象

    方法名功能
    boolean isPresent()判断是否包含对象
    void ifPresent(Consumer consumer)如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它

    获取Optional容器的对象

    方法名功能
    T get()如果调用对象包含值返回该值,否则抛异常
    T orElse(T other)如果有值(内部封装的t非空)则将其返回,否则返回指定的other对象
    T orElseGet(Supplier other)如果有值则将其返回,否则返回由Supplier接口实现提供的对象
    T orElseThrow(Supplier exceptionSupplier)如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
    @Test
    public void test26(){
        Boy boy = new Boy();
        boy = null;
        // 此会出现空指针异常
        String girlName = getGirlName(boy);
        System.out.println(girlName);
    }
    
    // 获取女孩的名字,容易出现空指针异常
    private String getGirlName(Boy boy) {
        return boy.getGirl().getName();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    @Test
    public void test27(){
        Boy boy = new Boy();
        boy = null;
        String girlName = getGirlName1(boy);
        System.out.println(girlName);
    }
    
    // 优化以后的getGirlName():
    public String getGirlName1(Boy boy){
        if(boy != null){
            Girl girl = boy.getGirl();
            if(girl != null){
                return girl.getName();
            }
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    使用Optional类

    @Test
    public void test28(){
        // 樱岛麻衣
        Boy boy = null;
        // 喜多川海梦
        boy = new Boy();
        // Lucy
        boy = new Boy(new Girl("Lucy"));
        String girlName = getGirlName2(boy);
        System.out.println(girlName);
    }
    
    //使用Optional类的getGirlName()
    public String getGirlName2(Boy boy){
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        //此时的boy1一定非空
        Boy boy1 = boyOptional.orElse(new Boy(new Girl("樱岛麻衣")));
        Girl girl = boy1.getGirl();
    
        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        //girl1一定非空
        Girl girl1 = girlOptional.orElse(new Girl("喜多川海梦"));
        return girl1.getName();
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    linux易忘命令(如:端口、进程、文件、内存、vim)持续更新
    Docker镜像制作
    操作指南|JumpServer用户权限体系的使用实践
    【AI设计模式】机器学习设计模式概述
    缓存的放置时间和删除时间
    双向链表的实现
    RabbitMq大纲
    线上问题处理案例:出乎意料的数据库连接池
    Double 4 VR仿真情景实训教学系统在商务谈判课堂上的应用
    深度学习第四课——卷积神经网络(week 2)
  • 原文地址:https://blog.csdn.net/qq_57005976/article/details/132744832