• java8新特性(上)-Lambda表达式


    1.Lambda表达式

    1.1为什么使用lambda表达式?

    Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

    1.2从匿名类到lambda表达式

    • 原来的内部类
     @Test
        public void test1() {
            Comparator<Integer> com = new Comparator<Integer>() {
                public int compare(Integer o1, Integer o2) {
                    return Integer.compare(o1, o2);
                }
            };
            //TreeSet需要拟传递一个匿名内部类进去,然后按照这个规则进行排序,最后输出的是一个有序的结果
            TreeSet<Integer> ts = new TreeSet<Integer>(com);
            ts.add(15);
            ts.add(20);
            ts.add(18);
            ts.add(30);
            ts.add(49);
            ts.add(16);
            ts.forEach(System.out::println);//输出结果:15 16 18 20 30 49
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 使用lambda表达式的匿名内部类
    @Test
        public void test2() {
            Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
            TreeSet<Integer> ts = new TreeSet<Integer>(com);
            ts.add(15);
            ts.add(20);
            ts.add(18);
            ts.add(30);
            ts.add(49);
            ts.add(16);
            ts.forEach(System.out::println);//输出结果:15 16 18 20 30 49
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.3Lmabda表达的优点体验

    • 前置条件,创建一个实体类,以便进行下面实验
      雇员实体类
    package entitys;
    import lombok.Data;
    import java.util.Objects;
    
    @Data
    public class Employee {
        private int id;
        private String name;
        private int age;
        private double salary;
        private Status status;
    	
    	//构造方法
        public Employee() {
        }
    	public Employee(int age) {
            this.age = age;
        }
    
        public Employee(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public Employee(String name, int age, double salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
        public Employee(int id, String name, int age, double salary, Status status) {
            this.id = id;
            this.name = name;
            this.age = age;
            this.salary = salary;
            this.status = status;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Employee)) return false;
            Employee employee = (Employee) o;
            return getAge() == employee.getAge() && Double.compare(employee.getSalary(), getSalary()) == 0 && Objects.equals(getName(), employee.getName());
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(getName(), getAge(), getSalary());
        }
    
        //建立一个枚举
        public enum Status{
            FREE,
            BUSY,
            VOCATION;
        }
    }
    
    • 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
    • 使用java自带的方法Arrays.asList把数组转集合,先创建一个eployee实体集合,以便进行实验
    List<Employee> employees = Arrays.asList(
                new Employee("张三", 18, 99999.9),
                new Employee("李四", 29, 99499.9),
                new Employee("王五", 22, 1999.9),
                new Employee("赵六", 26, 12999.9),
                new Employee("钱七", 38, 3999.9)
        );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    需求:获取年龄大于25岁的员工

    • 原来的写法
    public List<Employee> filterEmployees(List<Employee> employees) {
            List<Employee> emps = new ArrayList<>();
            for (Employee emp : employees) {
                if (emp.getAge() >= 35) {
                    emps.add(emp);
                }
            }
            return emps;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 改进写法1:使用策略者模式
      先创建一个 Mypredicate的接口,后面可以实现这个接口类的方法
    public interface Mypredicate<T> {
        public boolean test(T t);
    }
    
    • 1
    • 2
    • 3

    实现Mypredicate接口,通过这个实现接口进行过滤

    import entitys.Employee;
    //按照年龄进行过滤
    public class FilterEmployeeByAge implements Mypredicate<Employee>{
    
        @Override
        public boolean test(Employee t) {
            return t.getAge()>=20;
        }
    }
    //按照薪水进行过滤
    public class FilterEmployeeBysalary implements Mypredicate<Employee>{
        @Override
        public boolean test(Employee t) {
            return t.getSalary()>=2000;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    创建一个filterEmployee方法,里面调用Mypredicate的实现方法,进行过滤数据

    public List<Employee>filterEmployee(List<Employee>list,Mypredicate<Employee>mp){
            List<Employee>emps=new ArrayList<>();
            for (Employee employee :list) {
                if(mp.test(employee)){
                    emps.add(employee);
                }
            }
            return emps;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后实例化你需要过滤的方式的类,作为参数传递进filterEmployee方法进行过滤.

    //按照年龄进行过滤
    @Test
        public void test3(){
            //需要传递一个Mypredicate的实现类
            List<Employee>list=filterEmployee(employees,new FilterEmployeeByAge());
            for (Employee employee:list){
                System.out.println(employee);
            }
        }
    //按照薪水进行过滤
        @Test
        public void test4(){
            //需要传递一个Mypredicate的实现类
            List<Employee>list=filterEmployee(employees,new FilterEmployeeBysalary());
            //输出金额超过2000的对象
            for (Employee employee:list){
                System.out.println(employee);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 改进方式2:使用匿名内部类
     @Test
        public void test5(){
        //这里调用了改进方式1的filterEmployee方法,只是传递进去的参数使用匿名内部类进行实现
            List<Employee>list=filterEmployee(employees, new Mypredicate<Employee>() {
                @Override
                public boolean test(Employee employee) {
                    return employee.getSalary()<=50000;
                }
            });
            //输出金额超过2000的对象
            for (Employee employee:list){
                System.out.println(employee);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 改进方式3:使用lambda表达式
    @Test
        public void test6(){
        //这里调用了改进方式1的filterEmployee方法,只是传递进去的参数使用lambda表达式进行实现
            List<Employee>list=filterEmployee(employees,(employee -> employee.getSalary()<=5000));
            list.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 改进方式4:使用Stream API
     @Test
        public void test7(){
            employees.stream()
                    .filter((employee -> employee.getSalary()>=5000))//过滤
                    .limit(2)//取出前几个
                    .forEach(System.out::println);//输出
            System.out.println("-----------------------------------");
            //遍历所有的名字
            employees.stream()
                    .map(Employee::getName)
                    .forEach(System.out::println);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 总结
      上面的所有方法都是逐层递进的关系,可以发现越往后面走,方法越渐变,所写的代码也越来越少,通用性也越来越高,但是存在的问题就是代码的可读性逐渐降低,鱼和熊掌不可兼得,所以选择了效率必定也会有所损失,代码的维护性不高.

    1.4 Lambda表达式语法

    Lambda表达式在Java语言中引入了一个新的语法元素和操作符。这个操作符为“->”,该操作符被称为Lambda操作符或剪头操作符。它将Lambda分为两个部分:
    左侧:指定了Lambda表达式需要的所有参数
    右侧:指定了Lambda体,即Lambda表达式要执行的功能。

    语法格式一:无参,无返回值,Lambda体只需一条语句

    /*语法格式一:无参数,无返回值
                    ()->System.out.println("Hello Lambda!");*/
        int num = 0;//在1.7中,匿名内部类使用局部变量,需要加一个final,但是1.8不用加(其实是默认加了,所以不能对这个值进行修改),称之为糖衣于法
    
        @Test
        public void test1() {
            //以前最常见的就是Runnable()方法
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Hello lambda" + num);
                }
            };
            r.run();
            //使用lambda表达式的写法
            System.out.println("============================");
            Runnable runnable = () -> System.out.println("Hello lambda" + num);
            runnable.run();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    语法格式二:Lambda需要一个参数,此时参数的小括号可以省略

    /*语法格式2:有一个参数,无返回值
         * 若只有一个参数,参数的小括号可以省略不写,如果只有一个lambda体,大括号可以省
         * 左右遇一括号省
         * */
        @Test
        public void test2() {
        	//Consumer是java内置的函数式接口
            Consumer<String> con = (x) -> System.out.println(x);
            con.accept("加油学习Java");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    语法格式三:Lambda需要两个参数,并且有返回值

    /*语法格式3:有两个以上的参数,并且lambda体中有多条语句,并且有放回值
         * 如果有多条语句必须使用大括号
         * */
        @Test
        public void test3() {
        //Comparator是java内置的函数式接口
            Comparator<Integer> comparator = (x, y) -> {
                System.out.println("函数式接口");
                return Integer.compare(x, y);
            };
            System.out.println(comparator.compare(15, 20));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    语法格式四:当 Lambda 体只有一条语句时,return 与大括号可以省略

     /*语法格式4:有两个以上的参数,并且lambda体中只有一条语句,并且有放回值
         * 则大括号和return都可以省略
         * */
        @Test
        public void test4() {
        //Comparator是java内置的函数式接口
            Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
            System.out.println(comparator.compare(15, 20));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.5 推断类型

    上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”.

    /*语法格式5:lambda表达式的数据源的参数类型可以不写,如果写必须都写
         * 因为JVM编译器通过上下文推断出数据类型,称之为"类型推断"
         * 左侧推断类型省
         * */
        @Test
        public void test5() {
            Comparator<Integer> comparator = (Integer x, Integer y) -> Integer.compare(x, y);
            System.out.println(comparator.compare(45, 20));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.函数式接口

    函数式接口,就是指这个接口只有一个抽象方法的接口,并且在接口类上加上注解:@FunctionalInterface

    2.1带有一个参数的函数式接口

    • 例1:使用函数式接口进行数字计算
      创建一个函数式接口
    //带有一个抽象方法的接口称之为函数式接口
    @FunctionalInterface
    public interface MyFun {
        public Integer getValue(Integer num);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建一个方法operation,对函数式接口进行调用

    public Integer operation(Integer num, MyFun myFun) {
            return myFun.getValue(num);
        }
    
    • 1
    • 2
    • 3

    使用lambda表达式实现函数式接口

    @Test
        public void test6() {
            Integer num = operation(100, (x) -> x * x);
            System.out.println(num);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 例2:使用函数式接口对字符串进行操作
      创建一个函数式接口
    @FunctionalInterface
    public interface MyFunction {
        public String getValue(String str);
    }
    
    • 1
    • 2
    • 3
    • 4

    创建一个方法strHandler,对函数式接口进行调用

    public String strHandler(String str,MyFunction mf){
            return mf.getValue(str);
        }
    
    • 1
    • 2
    • 3

    声明函数式接口,接口中声明抽象方法,并对接口进行实现,然后调用strHandler,将接口实现方法传递进去;

     @Test
        public void test2(){
            //替换空格
            String str=strHandler("\t\t\t哈哈,我是大宝贝",(x)->x.trim());
            System.out.println(str);
            System.out.println("-------------------------------");
            //转换成大写
            str=strHandler("happy everyday",x->x.toUpperCase(Locale.ROOT));
            System.out.println(str);
            System.out.println("-------------------------------");
            //截取字符串
            str=strHandler("happy everyday",x->x.substring(2,5));
            System.out.println(str);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2带有泛型的函数式接口

    创建函数式接口

    //泛型类型为T为参数,R为返回值
    public interface MyFunction2<T, R> {
        public R getValue(T t1, T t2);
    }
    
    • 1
    • 2
    • 3
    • 4

    创建一个方法longHander,对函数式接口进行调用

     public void longHander(Long l1,Long l2,MyFunction2<Long,Long> myFunction){
            System.out.println(myFunction.getValue(l1,l2));
        }
    
    • 1
    • 2
    • 3

    声明函数式接口,接口中声明抽象方法,并对接口进行实现,然后调用strHandler,将接口实现方法传递进去,对两个long型数据进行处理;

    @Test
        public void test3(){
            longHander(12L,18L,(x,y)->x+y);
            longHander(12L,18L,(x,y)->x*y);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.Java 四大核心内置函数式接口

    函数式接口参数类型返回类型抽象方法用途
    Consumer:消费型接口Tvoidvoid accept(T t);操作数据
    Supplier:供给型接口TT get();创造数据
    Function:函数型接口TRR apply(T t);操作并返回数据
    Predicate:断言型函数接口TbooleanBoolean test(T t);判断数据

    3.1 Consumer:消费型接口

    //Consumer消费型接口
        public void shopping(Double money, Consumer<Double>consumer){
            consumer.accept(money);
        }
        @Test
        public void  test(){
            shopping(1000.0,x-> System.out.println(String.format("今天购物花费了%.2f元",x)));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.2 Supplier:供给型接口

    //Supplier供给型接口
        @Test
        public void test2(){
            //生成10个100以内的随机整数
            List<Integer>list=getNumber(10,()->(int)(Math.random()*100));
            list.forEach(System.out::println);
        }
        //需求:产生指定个数,并且放到集合中
        public List<Integer>getNumber(int num, Supplier<Integer>sup){
            List<Integer>list=new ArrayList<>();
            for(int i=0;i<num;i++){
                Integer n=sup.get();
                list.add(n);
            }
            return list;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.3 Function:函数型接口

    //Function函数型接口
        @Test
        public void test3(){
            String str=strHander("\t\t\t\t\t加油减肥  ",(x)->x.trim());
            System.out.println(str);
            System.out.println("=====================================");
            //截取字符串
            str=strHander("你是笨蛋我是傻瓜",(x)->x.substring(2,5));
            System.out.println(str);
        }
    
        //需求:用于处理字符串
        public String strHander(String str, Function<String,String>function){
            return function.apply(str);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.4 Predicate:断言型函数接口

     //Predicate断言型接口:
        @Test
        public void test4(){
            //将长度四个字的名字返回
            String[]names={"辣目洋子","东野圭吾","马尔科夫","史蒂芬","霍金","易烊千玺","雨果","曹禺","钱钟书","张爱玲"};
            List<String>list=new ArrayList<>(Arrays.asList(names));
            List<String>list1=filterStr(list,(x)->x.length()>=4);
            list1.forEach(System.out::println);
        }
        //需求:将满足条件的字符串,放入到集合中
        public List<String> filterStr(List<String>list, Predicate<String>pre){
            List<String>filter_data=new ArrayList<>();
            for(String str:list){
                if(pre.test(str)){
                    filter_data.add(str);
                }
            }
            return filter_data;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.其他接口

    函数式接口参数类型返回类型用途
    BiFunctionT, UR对类型为 T, U 参数应用操作, 返回 R 类型的结 果。包含方法为R apply(T t, U u);
    UnaryOperator (Function子接口)TT对类型为T的对象进行一元运算, 并返回T类型的结果。包含方法为T apply(T t);
    BinaryOperator(BiFunction 子接口)T, TT对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1, T t2);
    BiConsumerT,Uvoid对类型为T, U 参数应用操作。包含方法为void accept(T t, U u);
    ToIntFunction ,ToLongFunction ,ToDoubleFunctionTint ,long ,double分 别 计 算 int 、 long 、double、值的函数;
    IntFunction, LongFunction, DoubleFunctionint ,long ,doubleR参数分别为int、long、double 类型的函数;

    5.方法引用与构造器引用

    5.1 方法引用

    当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
    (实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)
    方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
    如下三种主要使用情况:

    • 对象::实例方法
    @Test
        public void test2(){
            Employee employee=new Employee();
            Supplier<String>sup=()->employee.getName();
            String str=sup.get();
            System.out.println(str);
    
            System.out.println("====================使用方法引用====================");
            Supplier<Integer>sup2=employee::getAge;
            System.out.println(sup2.get());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 类::静态方法
    //类::静态方法名
        @Test
        public void test3(){
            BiPredicate<String, String> bp = (x, y) -> x.equals(y);
            System.out.println("====================使用方法引用====================");
            BiPredicate<String, String> bp2 = String::equals;
            if(bp2.test("加油","加油")){
                System.out.println("相同");
            }else{
                System.out.println("不同");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 类::实例方法
     //对象::实例方法名
        @Test
        public void test1(){
            PrintStream printStream=System.out;
            Consumer<String> con=(x)->printStream.println(x);
            System.out.println("====================使用方法引用====================");
            con=printStream::println;
            //等价于
            con=System.out::println;
            con.accept("abcdefg");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.2 构造器引用

    在实体类中直接创建一个无参构造方法,可以直接通过构造器引用

    //构造器引用
        @Test
        public void test5(){
            Supplier<Employee>supplier=()->new Employee();
            //构造引用方法
            Supplier<Employee>sup2=Employee::new;
            Employee emp=sup2.get();
            System.out.println(emp);
        }
    
        @Test
        public void test6(){
            Function<Integer,Employee>function=(x)->new Employee(x);
            System.out.println("================使用方法引用=================");
            Function<Integer,Employee>fun2=Employee::new;
            Employee employee=fun2.apply(50);
            System.out.println(employee.toString());
            //BiFunction是Function的子类
            BiFunction<String,Integer,Employee>biFunction=Employee::new;
            Employee employee1=biFunction.apply("张三",100);
            System.out.println(employee1.toString());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5.3 数组引用

    //数组引用
        @Test
        public void test7(){
            Function<Integer,String[]>fun=(x)->new String[x];
            String[]strs=fun.apply(10);
            System.out.println(strs.length);//20
            //使用数组引用
            Function<Integer,String[]>fun2=String[]::new;
            String[] str2=fun2.apply(20);
            System.out.println(str2.length);//20
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    PAT(Advanced Level) Practice(with python)——1118 Birds in Forest
    【MATLAB源码-第38期】基于OFDM的块状导频和梳状导频误码率性能对比,不同信道估计方法以及不同调制方式对比。
    vue.js样式绑定
    深入理解常见的二十三种设计模式
    Java集中常见的排序
    sphinx匹配指定字段
    Java 接口的学习笔记
    java报错NoClassDefFoundError: Could not initialize class
    Redis——安装及使用
    2022年最新河北道路运输安全员模拟真题题库及答案
  • 原文地址:https://blog.csdn.net/qq_35653657/article/details/126768084