• Java高级---->JDK8新特性学习笔记


    1.什么是Lambda表达式

             1.Lambda 表达式是一种匿名函数,也可称为闭包,简单地说,它是没有声明的方法,即没有访问修饰符、返回值声明和名字。
             2.它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 语言的表达能力得到了提升。
             3.特殊的匿名内部类,语法更简洁
             4.允许把函数作为一个方法的参数,将代码像数据一样传递。

    2.Lambda表达式语法

    <函数式接口><变量名>=(参数1,参数2)->{
    	//方法体
    };
    
    • 1
    • 2
    • 3

    注意:

    	函数式接口:接口中只有一个抽象方法
    	(参数1,参数2):抽象方法的参数
    	->:表示分隔符
    	{}:表示抽象方法的实现
    
    • 1
    • 2
    • 3
    • 4
    public class Test02 {
        public static void main(String[] args) {
    
            //1.第一种方式:自定义接口实现runnable
            My my = new My();
            Thread t1 = new Thread(my);
            t1.start();
    
            //2.第二种 使用匿名内部类实现runnable
            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    System.out.println("匿名内部类实现runnable");
                }
            };
            Thread t2 = new Thread(r1);
            t2.start();
    
        }
    }
    class My implements Runnable{
    
        @Override
        public void run() {
            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

    分析代码:

    • Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心
    • 为了指定 run 的方法体,不得不需要 Runnable 接口的实现类
    • 为了省去定义一个 Runnable 实现类的麻烦,不得不使用匿名内部类
    • 必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错
    • 而实际上,似乎只有方法体才是关键所在。

    这时可以使用lambda表示完成上面的要求,Lambda更注重方法体的实现

     		//第三种 Lambda实现
            Runnable r2 = ()->{
                System.out.println("Lambda实现runnable");
            };
            Thread t3 = new Thread(r2);
            t3.start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    前提 : 必须是函数式接口。
    作用:简化匿名内部类的使用,语法更加简单

    2.1.Lambda简单实例

    1.无参无返回值

    public class Test03 {
        public static void main(String[] args) {
    
            //1.使用匿名内部类
            Swim swim = new Swim() {
                @Override
                public void Swimming() {
                    System.out.println("使用匿名内部类实现==");
                }
            };
    
            fun(swim);
    
    
            //2.使用Lambda表达式
            Swim swim1 = ()->{
                //Lambda表达式更注重方法体
                System.out.println("使用Lambda表达式实现");
            };
            fun(swim1);
    
        }
    
        public  static void fun(Swim swim){
            swim.Swimming();
        }
    }
    
    //自定义函数式接口
    interface Swim{
        public void Swimming();
    }
    
    • 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

    2.有参有返回值

    演示 java.util.Comparator 接口的使用场景代码,其中的抽象方法定义为:

    • public abstract int compare(T o1, T o2);

    当需要对一个对象集合进行排序时, Collections.sort 方法需要一个 Comparator 接口实例来指定排序的规则。

    public class Test04 {
        public static void main(String[] args) {
            List<Student> stuList  = new ArrayList<>();
            stuList.add(new Student("张三",18,"北京"));
            stuList.add(new Student("李四",17,"上海"));
            stuList.add(new Student("王五",19,"广州"));
    
            //第一种方式:使用匿名内部类实现
            Comparator<Student> c = new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    //按照年龄从小到大排列
                    return o1.getAge()-o2.getAge();
                }
            };
            Collections.sort(stuList,c);
            for (Student stu:stuList) {
                System.out.println(stu);
            }
            System.out.println("===========分割============");
            //第二种方式:使用Lambda表达式实现
            Comparator<Student> c1  =  (Student s1,Student s2)->{
                //按照年龄从大到小排列
                return s2.getAge()-s1.getAge();
            };
            Collections.sort(stuList,c1);
            for (Student stu:stuList) {
                System.out.println(stu);
            }
        }
    }
    
    
    class Student{
        private String name;
        private int age;
        private String address;
    
        public Student(String name, int age, String address) {
            this.name = name;
            this.age = age;
            this.address = address;
        }
    
        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 String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
    • 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

    2.2.Lambda表达式的详细介绍

    Lambda引入了新的操作符:->(箭头操作符), -> 将表达式分成两个部分
    		左侧:(参数1,参数2...)表示参数列表
    		右侧: {}内部是方法体
    
    注意事项:
    	1.形参列表的数据类型会自动推断
    	2.如果形参列表为空,只需要保留()
    	3.如果形参只有1个,()可以省略不写,只需要参数的名称即可
    	4.如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,则若想省去{},
    		则必须同时省去return,且执行语句也保证只有一句
    	5.Lambda不会生成一个单独的内部类文件
    
    
    第四条例子为: 
    Swim swim2 = ()-> System.out.println("执行语句只有一句,且无返回值,{}可以省略");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.函数式接口

    如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法上。

    @FunctionalInterface注解检测接口是否符合函数式接口

    内置函数式接口:

    public class Test02 {
        public static void main(String[] args) {
            Operater  o = arr -> {
                int sum=0;
                for (int n:arr) {
                    sum+=n;
                }
                System.out.println("数组的和为:"+sum);
            };
            fun(o);
        }
    
        public static void fun(Operater operater){
            int[] arr ={1,2,3,4};
            operater.getSum(arr);
        }
    }
    
    @FunctionalInterface
    interface Operater{
        public abstract void getSum(int[] arr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了使用Lambda方便,JDK提供了大量常用的函数式接口。

    常见的函数式接口
    在这里插入图片描述

    3.1Consumer < T >

    消费型接口即指该接口内的唯一抽象方法(void accept(T t) 方法) 只接受输入(有参数) ,没有返回值。只进不出。即有参数无返回值

    public class Test1 {
        public static void main(String[] args) {
            Consumer<Double> c = t->{
                System.out.println("吃饭花费了:"+t);
            };
            fun(c,200);
        }
    
        public static void fun(Consumer<Double> consumer,double money){
            consumer.accept(money);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class Test03 {
    
        public static void main(String[] args) {
            Consumer<Integer[]> o=arr -> {
                 int sum=0;
                 for(int n:arr){
                     sum+=n;
                 }
                System.out.println("数组的和为:"+sum);
            };
    
            fun(o);
        }
    
        public static void fun(Consumer<Integer[]> t){
             Integer[] arr={2,3,4,5,6,7,11};
             t.accept(arr);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.2 Supplier< T > 供给型函数式接口

    供给型接口即指该接口内的唯一抽象方法 无输入(无参数) ,有返回值。只出不进。
    T:表示返回结果的泛型

    public class Test3 {
        public static void main(String[] args) {
            Supplier<Integer> supplier = ()->{
                return new Random().nextInt(10);
            };
    
            //简写
            Supplier<Integer> supplier1 = ()->new Random().nextInt(10);
    
            fun(supplier);
            
            fun(supplier1);
    
        }
    
        public static void fun(Supplier<Integer> supplier){
            Integer res = supplier.get();
            System.out.println("内容为:"+res);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.3 Function 函数型函数式接口

    函数型接口即指该接口内的唯一抽象方法 既有输入(有参数) ,又有返回值。有出有进。

    T: 参数类型的泛型

    R: 函数返回结果的泛型

    例子: 传入一个字符串把小写转换为大写。

    public class Test05 {
        public static void main(String[] args) {
            Function<String,String> function = msg->{
                return msg.toUpperCase();
            };
            fun(function,"hello world");
    
            //简写
            fun(msg->msg.toUpperCase(),"hello world");
        }
    
        public static void fun(Function<String,String> function,String msg){
            String s = function.apply(msg);
            System.out.println(s);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.4 Predicated< T > : 断言型接口

    断言型接口即指该接口内的唯一抽象方法 有输入(有参数) ,且返回值必须为布尔值。有出有进。
    boolean test(T t);

    public class Test06 {
        public static void main(String[] args) {
            fun(name->name.length()>3?true:false,"去玩曹张新村");
        }
    
        public static void fun(Predicate<String> predicate,String name){
            boolean test = predicate.test(name);
            System.out.println("该名称的长度是否为长名称:"+test);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.方法引用

    4.1 lambda表达式解决代码冗余

    public class Test1 {
        public static void main(String[] args) {
            Consumer<Integer[]> c = arr->{
              int sum=0;
              for(int b:arr){
                  sum+=b;
              }
                System.out.println("数组的和为:"+sum);
            };
            fun(c);
        }
    
        public static void fun(Consumer<Integer[]> consumer){
            Integer[] arr = {1,2,3,4,5};
            consumer.accept(arr);
        }
    
        public static void sum(Integer[] arr){
            int sum=0;
            for (int a: arr) {
                sum+=a;
            }
            System.out.println("数组的和为:"+sum);
        }
    }
    
    • 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

    在这里插入图片描述
    分析:如果我们在Lambda中所指定的功能,已经有其他方法存在相同代码块,那就没必要重复写相同的代码了,因此可以直接“引 用” —方法引用。

    public class Test1 {
        public static void main(String[] args) {
            /*Consumer c = arr->{
              int sum=0;
              for(int b:arr){
                  sum+=b;
              }
                System.out.println("数组的和为:"+sum);
            };*/
            Consumer<Integer[]> c = Test1::sum;
            fun(c);
        }
    
        public static void fun(Consumer<Integer[]> consumer){
            Integer[] arr = {1,2,3,4,5};
            consumer.accept(arr);
        }
    
        public static void sum(Integer[] arr){
            int sum=0;
            for (int a: arr) {
                sum+=a;
            }
            System.out.println("数组的和为:"+sum);
        }
    }
    
    • 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.2 方法引用

    方法引用是Lambda表达式的一种简写形式,如果Lambda表达式方法体种只是调用一个特定的已经存在的方法,则可以使用方法引用。

    常见的形式:

    	1. 对象::实例方法
    	2. 类::静态方法
    	3. 类::实例方法
    	4. 类::new
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    4.2.1 实例方法引用

    实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。

    public class Test2 {
        public static void main(String[] args) {
            User user = new User("zs",18);
            Supplier<String> supplier = ()->user.getName();
            System.out.println("Lambda表达式输出结果:"+supplier.get());
    
            Supplier<String> supplier2 = user::getName;
            System.out.println("实例方法引用输出结果:"+supplier.get());
        }
    }
    
    class User {
    
        private String name;
        private Integer age;
    
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 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

    4.2.2 静态方法引用

    方法引用: 类名::静态方法
    Lambda表达式:(参数)->类名.静态方法(参数)

    public class Test2 {
        public static void main(String[] args) {
            Person[] people = {new Person("zs",18),new Person("ls",17),new Person("zl",19)};
            Arrays.sort(people,Person::compareByAge);
            for (Person p:people) {
                System.out.println(p);
            }
        }
    }
    
    class Person {
    
        private String name;
    
        private Integer age;
        //比较两个Person对象的年龄是否一致。
        public static int compareByAge(Person a, Person b) {
            return a.age.compareTo(b.age);
        }
    
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 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

    4.2.3 对象方法引用

     对象方法引用: 类名::实例方法.    (参数1,参数2)->参数1.实例方法(参数2)
    
    • 1

    若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。

    		//Function 传参且有返回值 适用于对象方法
    		Function<String,Integer> f1 = (str)->{
                return str.length();
            };
    
            Function<String,Integer> f2 = String::length;
    
            Integer qwe = f2.apply("qwe");
            System.out.println(qwe);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    	    //比较连个字符串是否相等
            //Lambda表达式
            BiFunction<String,String,Boolean> f6  = (o1,o2)->o1.equals(o2);
            Boolean apply1 = f6.apply("zs", "ls");
            System.out.println(apply1);
            //对象方法引用
            BiFunction<String,String,Boolean> f3=String::equals;
            Boolean apply = f3.apply("zs", "ls");
            System.out.println(apply);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    		// BiPredicate的test()方法接受两个参数,x和y,
            // 具体实现为x.equals(y),满足Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数
            BiPredicate<String,String> bp = (x, y) -> x.equals(y);
            BiPredicate<String,String> bp1 = String::equals;
    
            boolean test = bp1.test("xy", "xx");
            System.out.println(test);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.2.4 构造方法引用

    构造方法引用: 类名::new (参数)->new 类名(参数)

    	    Function<String,People> f4 = People::new;
            People zs = f4.apply("zs");
            System.out.println(zs);
    
            Function<String,People> f5 = s -> {
                return new People();
            };
    
            People ls = f5.apply("ls");
            System.out.println(ls);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    class People{
        String name;
    
        public People(String name) {
            this.name = name;
        }
    
        public People() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "People{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    • 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

    注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。

    Supplier<List<User>> userSupplier = () -> new ArrayList<>();
    List<User> user = userSupplier.get();
    Supplier<List<User>> userSupplier2 = ArrayList<User>::new;  // 构造方法引用写法
    List<User> user2 = userSupplier2.get();
    
    • 1
    • 2
    • 3
    • 4

    总结:

    (1)Lambda表达式
    	  语法: (参数列表)->{方法体}
      	  前提: 接口必须为函数式接口。
    (2)	函数式编程:
    	  JDK内置哪些函数式接口:
        	【1】消费型:有参无返回值。Consumer
        	【2】供给型:无参有返回值: Supplier
        	【3】函数型:有参有返回值: Function
        	【4】断言型:有参返回值为boolean ,Predicate
    (3)方法引用: 当lambda表达式有且仅有一条语句,而这条语句是调用了其他的方法。
    			这时可以考虑使用方法引用。::
     	[1]静态方法引用: 类名::静态方法     (参数)->类名.静态方法(参数)
     	[2]实例方法引用: 对象::实例方法     (参数)->对象.实例方法(参数)
     	[3]对象方法引用: 类名::实例方法.    (参数1,参数2)->参数1.实例方法(参数2)
     	[4]构造方法引用: 类名::new         (参数)->new 类名(参数)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.Stream流

    Java8的两个重大改变,一个是Lambda表达式,另一个是Stream API表达式。Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作.

    5.1 为什么使用Stream流

    当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历,通常我们用集合操作数据会呈现很多弊端。现需求如下:

    一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰,何线程
    需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
    
    • 1
    • 2
    public class My {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
            Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
    		// 1.拿到所有姓张的
            ArrayList<String> zhangList = new ArrayList<>(); // {"张无忌", "张强", "张三丰"}
            for (String name : list) {
                if (name.startsWith("张")) {
                    zhangList.add(name);
                }
            }
    		// 2.拿到名字长度为3个字的
            ArrayList<String> threeList = new ArrayList<>(); // {"张无忌", "张三丰"}
            for (String name : zhangList) {
                if (name.length() == 3) {
                    threeList.add(name);
                }
            }
    		// 3.打印这些数据
            for (String name : threeList) {
                System.out.println(name);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    以上代码太麻烦,因为每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环,每个需求都要循环一次,还要搞一个新集合来装数据。
    但是使用stream流就能很好的解决这种问题。

    public class Test1 {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
            Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰","何线程");
            // 1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
            list.stream()
                    .filter(item->item.startsWith("张"))
                    .filter(item->item.length()==3)
                    //此处发现该为对象引用方法所以可以简写 .forEach(System.out::println);
                    .forEach(item-> System.out.println(item));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    对集合的操作语法简洁,性能比传统快

    5.1 Stream流的原理

    注意:Stream和IO流(InputStream/OutputStream)没有任何关系

    Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
    在这里插入图片描述
    总之:Stream不存储数据,仅仅对数据进行加工。

    5.2 Stream流的操作步骤

    Stream有如下三个操作步骤:

    一、创建Stream

    从一个数据源,如集合、数组中获取流。

    二、中间操作

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

    三、终止操作

    一个终止操作,执行中间操作链,并产生结果。

    如:

    public class Test3 {
        public static void main(String[] args) {
            List<Person> personList = new ArrayList<>();
            personList.add(new Person("欧阳雪",18,"中国",'F'));
            personList.add(new Person("Tom",24,"美国",'M'));
            personList.add(new Person("Harley",22,"英国",'F'));
            personList.add(new Person("向天笑",20,"中国",'M'));
            personList.add(new Person("李康",22,"中国",'F'));
            personList.add(new Person("小梅",20,"中国",'F'));
            personList.add(new Person("何雪",21,"中国",'F'));
            personList.add(new Person("李康",22,"中国",'M'));
    
    		//找出性别为男的并输出
            personList.stream()
                    .filter(item->item.getSex()=='M')
                    .forEach(System.out::println);
        }
    }
    
    class Person {
        private String name;
        private Integer age;
        private String country;
        private char sex;
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", country='" + country + '\'' +
                    ", sex=" + sex +
                    '}';
        }
    
        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 getCountry() {
            return country;
        }
    
        public void setCountry(String country) {
            this.country = country;
        }
    
        public char getSex() {
            return sex;
        }
    
        public void setSex(char sex) {
            this.sex = sex;
        }
    
        public Person(String name, Integer age, String country, char sex) {
            this.name = name;
            this.age = age;
            this.country = country;
            this.sex = sex;
        }
    }
    
    • 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

    在这个例子中,personList.stream()是创建流,filter()属于中间操作,forEach是终止操作。

    5.3 获取Stream流的方式

    java.util.stream.Stream 是Java 8新加入的流接口。(并不是一个函数式接口)
    获取一个流非常简单,有以下几种常用的方式:

    1.	通过Collection对象的stream()【串行流】或者parallelStream()【并行流】
    2.	通过Arrays类的stream()方法
    3.	通过Stream接口的of()  、iterate()  、generate()方法
    4.	通过IntStream 、 LongStream 、 DoubleStream 接口中的 of 、range 、rangeClosed方法
    
    • 1
    • 2
    • 3
    • 4
    public class Test2 {
        public static void main(String[] args) {
            //1.通过集合对象调用stream()获取流
            ArrayList<String> list = new ArrayList<>();
            list.add("aa");
            list.add("bb");
            list.add("cc");
            list.add("dd");
            Stream<String> stream = list.stream();
    
            //2.通过Arrays数组工具类获取Stream对象
            int[] arr = {1,2,3};
            IntStream stream1 = Arrays.stream(arr);
    
            //3.使用stream类中的of方法
            Stream<String> stream2 = Stream.of("hello");
    
            //LongStream
            LongStream stream3 = LongStream.range(1, 10);
    
            //获取并行流
            Stream<String> stream4 = list.parallelStream();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.4 Stream流的中间操作

    中间操作api: 一个操作的中间链,对数据源的数据进行操作,而这种操作的返回类型还是一个Stream对象。

    5.4.1 中间操作–筛选与切片
    - filter:接收Lambda,从流中排除某些操作;
    - limit:截断流,使其元素不超过给定对象
    - skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,
    		 与limit(n)互补
    - distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    package demo02;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    /**
     * @ClassName: Test3
     * @Description: TODO
     * @Author: npc
     * @Date: 2022/7/20 20:28
     * @Version: v1.0
     */
    public class Test3 {
        public static void main(String[] args) {
            List<Person> personList = new ArrayList<>();
            personList.add(new Person("欧阳雪",18,"中国",'F'));
            personList.add(new Person("Tom",24,"美国",'M'));
            personList.add(new Person("Harley",22,"英国",'F'));
            personList.add(new Person("向天笑",20,"中国",'M'));
            personList.add(new Person("李康",22,"中国",'M'));
            personList.add(new Person("小梅",20,"中国",'F'));
            personList.add(new Person("何雪",21,"中国",'F'));
            personList.add(new Person("李康",22,"中国",'M'));
    
            //(1).  limit举例 -- 从Person列表中取出两个女性
            personList.stream().filter(person -> person.getSex()=='F')
                               .limit(2)
                               .forEach(System.out::println);
            //(2). skip举例 -- 从person列表中从第二个女性开始,取出所有的女性
            personList.stream().filter(person -> person.getSex()=='F')
                               .skip(1)
                               .forEach(System.out::println);
            //(3) distinct举例 从person列表中取出所欲的男性,并去除重复 男性中有两个李康,去除掉了一个重复的。
            personList.stream().filter(person -> person.getSex()=='M')
                                .distinct()
                                .forEach(System.out::println);
    
        }
    }
    
    class Person {
        private String name;
        private Integer age;
        private String country;
        private char sex;
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", country='" + country + '\'' +
                    ", sex=" + sex +
                    '}';
        }
    
        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 getCountry() {
            return country;
        }
    
        public void setCountry(String country) {
            this.country = country;
        }
    
        public char getSex() {
            return sex;
        }
    
        public void setSex(char sex) {
            this.sex = sex;
        }
    
        public Person(String name, Integer age, String country, char sex) {
            this.name = name;
            this.age = age;
            this.country = country;
            this.sex = sex;
        }
    }
    
    
    
    
    • 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
    5.4.2 Stream中间操作–映射
    • map --> 接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    • flatMap–> 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
    //map实例
    personList.stream()
                    .map(person -> {
                        Map<String,Object> map = new HashMap<>();
                        map.put("name",person.getName());
                        map.put("age",person.getAge());
                        return map;
                    })
                    .distinct()
                    .forEach(System.out::println);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    public class Test4 {
        public static void main(String[] args) {
            List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
                list.stream()
                    .map(Test4::getCharacterByString)
                        .forEach(System.out::println);
    
        }
    
        public static Stream<Character> getCharacterByString(String str){
            List<Character> characterList = new ArrayList<>();
            for (Character c : str.toCharArray()){
                characterList.add(c);
            }
            return characterList.stream();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这种流中流打印结果为

    java.util.stream.ReferencePipeline$Head@7ba4f24f
    java.util.stream.ReferencePipeline$Head@3b9a45b3
    java.util.stream.ReferencePipeline$Head@7699a589
    java.util.stream.ReferencePipeline$Head@58372a00
    
    • 1
    • 2
    • 3
    • 4

    若我们继续使用map打印 需要打印两次

    List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
            Stream<Stream<Character>> streamStream = list.stream()
                    .map(Test4::getCharacterByString);
            streamStream.forEach(sm->sm.forEach(System.out::print));
    
    • 1
    • 2
    • 3
    • 4

    但是如果使用flatMap就不需要打印两次 因为它将流中的每个值都换成另一个流,然后把所有流连接成一个流

    		List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
            list.stream()
                    .flatMap(Test4::getCharacterByString)
                    .forEach(System.out::print);
    
    • 1
    • 2
    • 3
    • 4
    5.4.3 Stream中间操作–排序
    • sorted() – 自然排序(Comparable)
    • sorted(Comparator c) – 定制排序(Comparator)
    //sorted()
    personList.stream()
                      .sorted(((o1, o2) -> o1.getAge()- o2.getAge()))
                      .forEach(System.out::println);
    
    • 1
    • 2
    • 3
    • 4
     personList.stream()
                    .sorted((o1,o2)->{
                        if(o1.getAge().equals(o2.getAge())){
                            return o1.getName().compareTo(o2.getName());
                        }else {
                            return o1.getAge()-o2.getAge();
                        }
                    })
                    .forEach(System.out::println);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5.5 终止操作–查找与匹配

    • allMatch–检查是否匹配所有元素
    • anyMatch–检查是否至少匹配一个元素
    • noneMatch–检查是否没有匹配所有元素
    • findFirst–返回第一个元素
    • findAny–返回当前流中的任意元素
    • count–返回流中元素的总个数
    • max–返回流中最大值
    • min–返回流中最小值
    //(8) allMatch--全都匹配上才为true 是否都为中国人
            boolean b = personList.stream()
                    .allMatch(person -> person.getCountry().equals("中国"));
            System.out.println(b);
            //(9)anyMatch--至少有一个匹配上才为true
            boolean b1 = personList.stream()
                    .anyMatch(person -> person.getCountry().equals("中国"));
            System.out.println(b1);
            //(10) noneMatch 是否有无没有匹配的所有元素
            boolean b2 = personList.stream()
                    .noneMatch(person -> person.getCountry().equals("中国"));
            System.out.println(b2);
            //(10) findFirst-->返回匹配的第一个元素 串行流则一个一个匹配
            Person person1 = personList.stream()
                    .filter(person -> person.getSex() == 'M')
                    .findFirst()
                    .get();
            System.out.println(person1);
    
            //(11) findAny-->数据足够大的时候 使用并行流 返回任意一个匹配的元素 使用串行流则匹配第一个
            Person person2 = personList.parallelStream()
                    .filter(person -> person.getSex() == 'M')
                    .findAny()
                    .get();
            System.out.println(person2);
    
            //(12)count -- 返回流中元素的总个数 男生的个数
            long count = personList.parallelStream()
                    .filter(person -> person.getSex() == 'M')
                    .count();
            System.out.println(count);
    
            //(13)max--返回流中最大值
            Optional<Person> max = personList.stream()
                    .max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
            System.out.println(max.get());
    
            //(14)min--返回流中最小值
            Optional<Person> min = personList.stream()
                    .min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
            System.out.println(min.get());
    
    • 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
  • 相关阅读:
    OneDrive 同步应用的网络使用率规划
    面试 Python 基础八股文十问十答第七期
    嵌入式工程师成长之路
    接口测试,
    玄机科技闪耀中国国际动漫节,携手百度共绘 AI 国漫新篇章
    【Unity3D】UGUI回调函数
    《重构代码设计》
    flex布局相关总结&&实现元素框居中效果
    Redis持久化:RDB与AOF深入解析
    Kubernetes 集群中日志采集的几种玩法¶
  • 原文地址:https://blog.csdn.net/weixin_44720982/article/details/125878846