java函数式、lambda表达式、Stream流的原理,关系和应用

一  背景

jdk7到jdk8引入了很多新的特性,函数式接口,新的Date API,stream流,lambda表达式,接口默认方法等。在这里主要对函数式接口、lambda表达式、stream流做简单的记录学习。欢迎大家一起学习交流。

二  函数式接口的概念

函数式接口一句话总结:只有一个抽象方法的接口称为函数式接口。可以用【@FunctionalInterface】注解检验是否是函数式接口,在jvm进行编译的的时候会检查接口是否符合函数式接口规范。

函数式接口的几点特征:

  • 函数式接口只有一个抽象方法
  • 接口的default方法某默认实现,不属于抽象方法
  • 接口重写了Object的公共方法也不算结接口的抽象方法

这里就可以解释Comparator为什么是函数式接口。

三  lambda表达式

lambda表达式概念:lambda表达式可以理解为函数式接口的隐式转换,也是函数中的匿名类(闭包)。只有拥有函数式接口才能写lambda表达式。

lambda表达式的常见形式:

1  没有参数的lambda:

    private static void testNoParamLambda() {
        // lambda方式
        Runnable noArguments = () -> System.out.println("Hello World");
        // 闭包方式
        Runnable noArguments = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World");
            }
        };
    }

这个Lambda表达式不包含参数,使用空括号 () 表示没有参数。Runnable接口也只有一个 run 方法,没有参数,且返回类型为 void

2  单个参数的lambda:

    private static void testSingleParamLambda() {
        // lambda方式
        Predicate predicate = (number) -> number > 2;
        // 闭包方式
        Predicate predicate = new Predicate() {
            @Override
            public boolean test(Integer integer) {
                return integer > 2;
            }
        };
    }

这个Lambda表达式包含一个参数,参数是Integer,返回值是Predicate,注意Predicate可以理解为是实现函数式接口的匿名内部类的对象。通过predicate这样调用这个方法。

java函数式、lambda表达式、Stream流的原理,关系和应用_第1张图片 3  多个参数的lambda:

    private static void testDoubleParamLambda() {
        // lambda方式
        BinaryOperator binaryOperator = (s1, s2) -> s1 + "love" + s2;
        // 闭包方式
        BinaryOperator binaryOperator = new BinaryOperator() {
            @Override
            public String apply(String s, String s2) {
                return s + "love" + s2;
            }
        };
    }

lambda表达式的特点:

  • 没有方法名(等同于闭包匿名函数)
  • 不需要声明参数类型,编译器可以根据上下文推断出参数的类型
  • 如果lambda只有一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 如果主体包含了一个语句,就不需要使用大括号,多行语句需要大括号。
  • 如果主体只有一个表达式返回值则编译器会自动返回值,如果有大括号,需要指定表达式的返回值

lambda表示自定义的函数接口

函数式接口:

/**
 * @description: 函数式接口
 */
@FunctionalInterface
public interface FunctionInterface {

    U packStr(T t1, R r2, T t3, R r4);
}

测试代码:

    private static void testCustomLambda() {
        // lambda方式
        FunctionInterface functionInterface =
                (s1, s2, s3, s4) -> "第一个数字是" + s1 + "第二个数字是" + s2 + "第三个数字是" + s3 + "第四个数字是" + s4;
        System.out.println(functionInterface.packStr(1, 2L, 3, 4L));
        // 闭包方式
        FunctionInterface functionInterface1 = new FunctionInterface() {
            @Override
            public String packStr(Integer s1, Long s2, Integer s3, Long s4) {
                return "第一个数字是" + s1 + "第二个数字是" + s2 + "第三个数字是" + s3 + "第四个数字是" + s4;
            }
        };
    }

四  jdk8的Stream流

Stream流简介:

Stream流是jdk8为了更方便对集合类的迭代而产生的,通过Stream流你可以实现对集合的遍历,分组,过滤,排序,集合类型转化,找到集合(主要是list和set)中的极值等等。

Stream流特点:

  • Stream并不是某种数据结构,它更像是数据源的一种迭代器(Iterator),单向、不可重复。
  • Stream数据源只能遍历一次。Stream通常调用对应的工具方法创建,如:list.stream()。
  • Stream流操作共分为两个大类:惰性求值及时求值。及时求值有foreach,collect操作。只有,当且仅当存在及时求值(终端操作)时,惰性求职(中间操作)操作才会被执行。

五  stream流,函数式接口,lambda的关系

stream流的很多方法都是和函数式接口有关的。而函数式接口又可以通过lamdba表示。stream和常见的函数式接口有如下配合。

stream的方法 函数式接口 函数式接口方法 方法描述
filter Predicate boolean test(T t) 根据输入的T值得到布尔值,主要用于stream筛选元素
allMatch Predicate boolean test(T t) 根据输入的T值得到布尔值,主要用于stream匹配元素
foreach Consumer void accept(T t) 消费T类型的消息,主要用于stream遍历元素
map Function R apply(T t) 将T类型值转换成R类型值,主要用于stream转换元素
sorted Comparator int compare(T o1, T o2) 根据输入的T、T两值得到int值,主要用于stream排序元素
reduce BinaryOperator T apply(T t, T u) BiFunction的特例实现,主要用于stream合并元素

六  配合应用举例

@Data
    @AllArgsConstructor
    public static class Person {

        private Integer age;

        private String name;

    }
private static void testStreamUsage() {
        Person a = new Person(10, "A");
        Person b = new Person(13, "B");
        Person c = new Person(15, "C");
        Person d = new Person(18, "D");
        // 打印出年纪最大的人的名字
        System.out.println(Stream.of(a, c, b, d).max((s1, s2) -> s2.getAge() > s1.getAge() ? -1 : 0).map(Person::getName).orElse("null"));

        // 打印出年纪最小的人的名字
        System.out.println(Stream.of(a, c, b, d).min((s1, s2) -> s1.getAge() > s2.getAge() ? -1 : 0).map(Person::getName).orElse("null"));

        // 按年龄大小(从大到小)打印所有人的名字
        Stream.of(a, c, b, d).sorted((s1, s2) -> s2.getAge() > s1.getAge() ? -1 : 0).map(Person::getName).forEach(System.out::println);
    }

java函数式、lambda表达式、Stream流的原理,关系和应用_第2张图片

上述代码演示了stream常见的方法,有兴趣的小伙伴还是要自己实践下。

七  本文参考

java Comparator为何是函数式接口?

Java 8 函数式接口 | 菜鸟教程

JDK 8 函数式编程入门 - alfred_zhong - 博客园

Java 函数式编程(Lambda表达式)与Stream API - March On - 博客园

Java8 Stream流 - OKevin - 博客园

[译] 一文带你玩转 Java8 Stream 流,从此操作集合 So Easy - 掘金

你可能感兴趣的:(Java,java,开发语言,后端)