13Lambda、函数式接口、Stream

13Lambda、函数式接口、Stream

1、Lambda表达式

Lambda表达式的介绍与使用

匿名内部类的格式:
new 父类或接口() {
//重写父类或接口的方法
}
匿名内部类很多地方是冗余的。
比如使用匿名内部类的方式实现多线程。
因为Thread构造方法参数需要一个Runnable类型的数据,所以我们不得不写new Runnable。
因为要重写Runnable中的run方法,所以又不得不写了run方法等声明
整个匿名内部类中最关键内容是方法,方法最重要的东西有前中后三点。
前: 参数。
中: 方法体
后: 返回值
最好的情况是我们只关注匿名内部类中这些最核心的东西(方法的参数,方法体,返回值)。
Lambda表达式只关注方法的参数,方法体,以及返回值。
Lambda表达式是匿名内部类的简化写法。
Lambda表达式是属于函数式的编程思想

面向对象思想: 怎么做
函数式编程思想: 做什么
匿名内部类的好处是可以省略单独创建.java文件的操作。但是匿名内部类也有缺点,就是语法有些冗余。匿名内部类中最核心的东西是方法的参数,方法体,返回值,其他的内容都是为了满足格式不得不写的一些东西。匿名内部类的简化写法是lambda表达式,lambda表达式只需要关注方法的参数,方法体,返回值。

  • Lambda的标椎格式:
    (参数类型 参数名)->{
    方法体;
    return 返回值;
    }
    格式解释:
    (1)小括号中的参数和之前的方法的参数一样,多个参数之间要使用逗号隔开。
    (2)->箭头是一个运算符,表示指向性的动作。
    (3)大括号中的内容和之前的方法大括号中的内容是一样的。

Lambda表达式可以省略面向对象中的一些条条框框,让我们只关注最核心的内容。
使用了函数式编程的思想:可推导,可以省略。

因为Thread构造方法中需要Runnable接口类型的参数,所以可以省略new Runnable。同时因为Runnable中只有一个 抽象方法run,所以重写的必然是这个抽象方法,那么也可以省略run方法的声明部分。

使用比较器排序,对保存学生对象的集合根据年龄升序排序。

public static void main(String[] args) {
        //创建集合
        List<Student> list = new ArrayList<>();
        //添加学生对象
        list.add(new Student("嫐", 20));
        list.add(new Student("嬲", 18));
        list.add(new Student("挊", 22));
        //使用比较器排序,对集合中的学生对象根据年龄升序排序。
        //Collections.sort(list, new Rule());
        //进行比较器排序,使用匿名内部类的方式
        /*
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        */
        //进行比较器排序,使用Lambda表达式
        /*
        Collections.sort(list, (Student o1, Student o2) -> {
            return o1.getAge() - o2.getAge();
        });
        */
        Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());
        //输出集合
        System.out.println(list);
}
  • Lambda表达式的标准格式:
    (参数类型 参数名)->{
    方法体;
    return返回值;
    }
    省略规则:小括号中的参数类型可以省略;
    如果小括号中只有一个参数,那么可以省略小括号。
    如果大括号中只有一条语句,那么无论该方法有没有返回值,都可以省略大括号。
  • 接口:
package cn.itcast.demo01_lambda;

public interface MyInterface {
    //提供抽象方法
    void printStr(String str);
}
  • 测试类
public class Test {
    public static void main(String[] args) {
        method(new MyInterface() {
            @Override
            public void printr(String string) {
                System.out.println(string);
            }
        });

        method((String str) ->{
            System.out.println(str);
        });
        

        method(str -> System.out.println(str));
    }

    public static void method(MyInterface myInterface) {
        myInterface.printr("hello");
    }
}

Lambda表达式的使用前提

(1)必须要有接口(不能是抽象类),接口中必须有且只有一个需要被重写的抽象方法。
(2)必须支持上下文推导。要能够推导出来这个Lambda表达式表示的是哪个接口中的内容。常用的一种上下文推导方式是使用Lambda表达式当做方法参数(传递给一个接口类型)也可以将Lambda表达式赋值给一个接口类型的变量。

public static void method(MyInterface m) {
        m.printStr("hello");
    }

    public static void main(String[] args) {
        //调用method方法
        //method(str -> System.out.println(str));

        //使用匿名内部类方式创建对象
        /*
        MyInterface m = new MyInterface() {
            @Override
            public void printStr(String str) {
                System.out.println(str);
            }
        };
        */
        //将Lambda表达式赋值给接口类型。
        MyInterface m = str -> System.out.println(str);
        m.printStr("HELLO");
}

函数是接口

函数式接口的介绍

如果一个接口中有且仅有一个需要被重写的抽象方法,那么这个接口就是一个函数式接口。函数式接口可以当做Lambda表达式的使用前提。
有一个注解叫做@FunctionalInterface可以验证一个接口是否是函数式接口, 如果在接口上面加上@FunctionalInterface注解报错,那么就说明该接口不是函数式接口,如果在接口上面加上@FunctionalInterface注解不报错,那么就说明该接口是一个函数式接口。

@FunctionalInterface仅仅用来验证一个接口是否是函数式接口,如果不使用该注解,只要接口符合函数式接口的规则,那么他就是一个函数式接口。
在jdk8的时候,提供了java.util.function,这个包下面有大量的函数式接口。
其中有一个函数式接口叫做Consumer,可以把它看成一个消费者,可以去消费(使用)一个数据。

  • 抽象方法:void accept(T t):用来消费(使用)一个数据。
public class Demo01Consumer {
    //定义方法,使用函数式接口Consumer当做参数
    public static void method(Consumer<String> c) {
        //通过c调用accept方法,使用字符串
        c.accept("hello");
    }

    public static void main(String[] args) {
        //调用method方法,传递匿名内部类对象。
        /*
        method(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        */
        //调用method方法,传递Lambda表达式
        method(s -> System.out.println(s));

    }
}
  • 在Java中还有一个函数式接口叫做Predicate,这个函数式接口可以判断一个数据是否符合要求。
    抽象方法:boolean test(T t): 判断一个数据t是否符合要求。
public class Demo02Predicate {
    //定义方法,使用函数式接口Predicate当做方法参数
    public static void method(Predicate<String> p) {//p= s -> s.length() == 5
        //调用方法,判断hello这个字符串是否符合要求
        boolean flag = p.test("hello");
        System.out.println(flag);
    }

    public static void main(String[] args) {
        //调用method方法
        method(s -> s.length() == 5);
    }
}

Stream流

Stream流的获取

Stream是一个接口,这个接口表示的就是流。
获取Stream流的两种方法:
(1)通过Collection单列集合获取。
(2)通过数组获取流。

  • 通过Collection单列集合获取流:
    在Collection中有一个方法,叫做stream,可以得到集合对应的流。
    Stream stream():获取集合对应的流。
public class Demo02CollectionGetStream {
    public static void main(String[] args) {
        //创建List集合
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("aa");
        list.add("bb");
        list.add("cc");
        //调用集合的stream方法,获取流
        Stream<String> stream = list.stream();

        //调用stream的toArray将流转成数字,然后再使用数组的工具类将内容输出
        System.out.println(Arrays.toString(stream.toArray()));
    }
}
  • 通过数组获取流:
    (1)使用Stream中的静态方法of获取。
    static Stream of(T… values): 根据数组获取流,参数是可变参数,可以传递多个数据,也可以传递数组。
    (3)使用Arrays工具类中的stream方法获取。
public class Demo03ArrayGetStream {
    public static void main(String[] args) {
        //1. 使用Stream中的静态方法of获取
        String[] strArr = {"aa", "bb", "cc", "dd"};
        //static Stream of(T... values): 根据数组获取流
        //Stream<String> stream = Stream.of(strArr);
        Stream<String> stream = Stream.of("hello", "world", "java");
        //输出Stream流中的内容
        System.out.println(Arrays.toString(stream.toArray()));

        //2. 使用Arrays工具类中的stream方法获取
        Stream<String> stream2 = Arrays.stream(strArr);
        System.out.println(Arrays.toString(stream2.toArray()));
    }
}

Stream流的常用方法

  • (1)forEach:在Stream中,有一个方法叫做forEach,可以对流中的元素进行逐一处理。
    void forEach(Consumer action):参数需要传递Consumer类型,Consumer表示消费者。
    forEach方法的参数定义的就是处理的规则(如何进行处理)
    forEach方法参数是函数式接口Consumer类型, 那么我们可以传递Lambda表达式, 这个Lambda表达式表示的含义是如何处理。
  • (2)在Stream中,有一个方法叫做filter,可以对流中的元素进行过滤(筛选)
    Stream filter(Predicate predicate):对流中的元素进行过滤(筛选)
    filter方法参数需要一个Predicate类型,Predicate这个函数式接口的作用是用来判断一个数据是否符合要求。
    我们要在参数Predicate中定义过滤的规则,如果判断成立,那么表示该数据留下,如果判断不成立表示该数据过滤掉。
    filter方法参数是Predicate函数式接口,那么我们可以传递Lambda表达式, 该Lambda表达式表示的是Predicate中
    唯一的抽象方法test的内容(重写后), 我们要在Lambda表达式中定义过滤规则,如果条件成立,那么数据不会过滤掉,
    如果条件不成立,那么数据会被过滤掉。
  • (3)在Stream中,有一个方法叫叫做count,可以获取流中元素的个数
    long count(): 获取流中元素的个数
  • (4)在Stream中,有一个方法叫做limit,可以获取流中的前几个元素
    Stream limit(long n): 获取流中的前n个元素。
  • (5)在Stream中,有一个方法叫做skip,可以跳过流中的前几个元素。
    Stream skip(long n):跳过流中的前n个元素
  • (6)在Stream中,有一个静态方法叫做concat,可以对两个流进行合并。
    static Stream concat(Stream a, Stream b): 对参数a和b这两个流进行合并,合并成新的流。
  • 注意:流的注意事项:
    1. 调用Stream的非终结方法会返回Stream流对象,但是返回的Stream是一个新的流。(forEach、count为终结方法,不能连接操作)
    2. 流只能一次性使用。

猜你喜欢

转载自blog.csdn.net/weixin_45507013/article/details/99314681
今日推荐