栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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其实是比较输入的值是否大于2的方法。你可以这样调用这个方法。

 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表示自定义的函数接口

函数式接口:

@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的方法函数式接口函数式接口方法方法描述
filterPredicateboolean test(T t)根据输入的T值得到布尔值,主要用于stream筛选元素
allMatchPredicateboolean test(T t)根据输入的T值得到布尔值,主要用于stream匹配元素
foreachConsumervoid accept(T t)消费T类型的消息,主要用于stream遍历元素
mapFunctionR apply(T t)将T类型值转换成R类型值,主要用于stream转换元素
sortedComparatorint compare(T o1, T o2)根据输入的T、T两值得到int值,主要用于stream排序元素
reduceBinaryOperatorT 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);
    }

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

七  本文参考

java Comparator为何是函数式接口?

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

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

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

Java8 Stream流 - OKevin - 博客园

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489332.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号