• lamdba表达式& 函数式接口& stream 整理


    lamdba表达式& 函数式接口& stream流 整理

    lamdba,函数式接口(函数式编程) 是JDK8 的新特性,

    • 在开发中,可以使得代码更加的简洁;
    • 消除一定量的嵌套
    • 可读性提高(方法名称 fitler ,map .distinct ,count 等)
    • 大数据集合处理效率提升

    java 的编程思想是一切都是对象,我们在写代码的时候也注重一个方法是有一个对象来完成的;

    而函数式编程更加关注的是函数(方法)本身,关注是函数做了什么事情;

    1.什么是lamdba表达式

    官方释义:

    A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters.
    lambda表达式就像一个方法:它提供了一个形式参数列表和一个用这些参数表示的主体(表达式或块)。
    
    • 1
    • 2

    lamdba 是JDK8 提供的语法糖,对匿名的内部类写法 进行 简化. 是函数式编程思想的一个体现

    1.1 常用写法

    格式: (参数列表)->{代码块} 其参数列表或者方法列表是推断出来的

    下面就是一般的表达式的写法;

    lambda表达式的求值产生函数接口的实例。Lambda表达式求值不会导致表达式正文的执行;相反,这可能在稍后调用函数接口的适当方法时发生。

    //没有参数时的表达式 多行代码需要使用{{}}
    () -> {}                // No parameters; result is void  没有参数 没有返回值(或者是返回值是void)
    () -> 42                // No parameters, expression body   
    () -> null              // No parameters, expression body
    () -> { return 42; }    // No parameters, block body with return
    () -> { System.gc(); }  // No parameters, void block body
    
    () -> {                 // Complex block body with returns
      if (true) return 12;
      else {
        int result = 15;
        for (int i = 1; i < 10; i++)
          result *= i;
        return result;
      }
    }                          
    
    //一个参数的时候 使用()包起来无所谓  
    (int x) -> x+1              // Single declared-type parameter
    (int x) -> { return x+1; }  // Single declared-type parameter
    (x) -> x+1                  // Single inferred-type parameter
    x -> x+1                    // Parentheses optional for
                                // single inferred-type parameter
    
    (String s) -> s.length()      // Single declared-type parameter
    (Thread t) -> { t.start(); }  // Single declared-type parameter
    s -> s.length()               // Single inferred-type parameter
    t -> { t.start(); }           // Single inferred-type parameter
    
    //要不全部声明参数类型 要不都不声明 声明部分在编译时会报错
    (int x, int y) -> x+y  // Multiple declared-type parameters
    (x, y) -> x+y          // Multiple inferred-type parameters
    (x, int y) -> x+y    // Illegal: can't mix inferred and declared types
    (x, final y) -> x+y  // Illegal: no modifiers with inferred types
    
    • 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

    案例一 :

    // 会自动推导出类型是Runnable 方法体是run 方法
    new Thread(() -> {
                System.out.println("start");
            }).start();
            
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("start");
                }
            }).start();
    
    //可以看到 Runnable 是一个函数式接口  
    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.2.lamdba 表达式的参数

    lamdba 表达式的参数要不是声明好类型,要不是推断的类型,但是不能混用(多个参数,声明其中部分类型)

    • 声明参数类型:lambda表达式称为显式类型
    • 不声明参数类型:lambda表达式称为隐式类型

    零参数的lambda表达式是显式类型

    参数类型声明的时候注意:

    • 要不全部声明参数类型 要不都不声明 声明部分在编译时会报错
    • 一个参数的时候 使用()包起来无所谓

    1.2 lamdba 表达式的方法体

    单个表达式或块。与方法体一样,lambda体描述每当调用发生时将执行的代码;

    //void 类型返回
    () -> {}
    () -> { System.out.println("done"); }
    
    // 有返回值
    () -> { return "done"; }
    () -> { if (...) return 1; else return 0; }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    lambda表达式中使用但未声明的任何局部变量、形式参数或异常参数必须声明为final或有效final,否则在尝试使用时发生编译时错误;

    : 什么是有效final ? 也叫efftive final ; 他没有final 显示的修饰,并且后面不会对它二次赋值(官方文档的描述是是不在赋值预算符的左侧,或者是递增递减的前缀和后缀[++ – ]); 并且已经初始化

    1.3 lamdba 表达式的数据类型

    如果 T 是一个函数接口类型;,并且该表达式与从 T 派生的地面目标类型的函数类型一致,则 lambda 表达式在赋值上下文、调用上下文或强制转换上下文中与目标类型 T 兼容。

    目标类型由 T 推导如下:

    如果 T 是通配符参数化的函数接口类型,并且 lambda 表达式是显式类型的,那么将按照描类型描述述断目标类型。

    如果 T 是一个通配符参数化的函数接口类型,而 lambda 表达式是隐式类型的,那么 ground 目标类型是 t 的非通配符参量化;

    2.函数式接口

    函数式接口:任何接口,如果只包含唯一一个抽象方法(除了 Object 的 public 方法之外) ,那么它就是一个函数式接口。

    2.1 案例判断

    //是
    interface Runnable {
        void run();
    }
    //不是
    interface NonFunc {
        boolean equals(Object obj); //Object 方法
    }
    
    //是的
    interface Func extends NonFunc {
        int compare(String o1, String o2); //唯一的 非object 的一个抽象方法
    }
    
    //所以类似 java.util.Comparator 是一个函数式接口
    interface Comparator<T> {
        boolean equals(Object obj); // Object 方法
        int compare(T o1, T o2);
    }
    
    //两个接口  clone 是Object 的非 public 的
    interface Foo {
        int m();
        Object clone();
    }
    
    • 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

    如果有两个方法(FI5 ) ,

    • m 的签名是 M 中每个方法签名的子签名
    • m 方法返回值是可以替代类型 如 FI2.m 的方法返回值签名Iterable 可以替代 FI3.m() 的 Iterable
    		@FunctionalInterface
        public interface  FI2  {
            Iterable m(Iterable<String> arg);
        }
    
        @FunctionalInterface
        public interface  FI3 {
            Iterable<String> m(Iterable arg);
        }
    
        @FunctionalInterface  // 也是一个函数式接口
        interface FI5 extends FI2, FI3 { /  /这个也是  FI2.m 的方法签名可以替代 Iterable<String>
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注意:@FunctionalInterface 作用,用于编译器校验你的接口是不是函数式接口

    2.2 实例化

    对于函数式接口:

    • 通过声明和实例化类(15.9)来创建接口实例的常规过程

    • 还可以使用方法引用表达式和 lambda 表达式来创建函数接口实例。

        public static void main(String[] args) {
          //常规过程
            FI fi = new FIDemo();
            fi.fun1();
    			// lambda 表达式
            fi = ()-> System.out.println("FIDemo2");
            fi.fun1();
        }
    
    
       static class FIDemo implements FI{
    
            @Override
            public void fun1() {
                System.out.println("FIDemo1");
            }
        }
    
        public interface  FI {
            void fun1();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.3 常用的函数式接口

    java.util.function 包下面的四大种类

     				//供给型 生产型   没有参数  有输出的  
            Supplier<Integer> sup = () -> {
                return 100;
            };
    
            //消费型 有输入 没有输出的  消费型(消费输入)
            Consumer<String> consumer = (x) -> {
                System.out.println(" return void" + x);
            };
    
            //有输入也有输出的  函数型接口 (第一个泛型是输入,第二个泛型是输出)  计算转换类型
            Function<String, Integer> function = (x)-> x.length();
    
          
            //判断型接口
            Predicate<String> predicate = (x)->{
                boolean b = x.length() > 5;
                return b;
            };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.Stream流

    JDK8 的Stream 使用函数式编程模式,可以使我们对集合的操作就像对流一样链式操作;简化和方便对 集合的操作;

    流的操作可以氛围三大部分

    • 获取流,
    • 流中间操作
    • 输出流中数据 也就是终结流

    3.1 Stream流的创建/获取

    //链表获取流
    List<User> users = getUsers();
    Stream<User> stream = users.stream();
    
    //数组获取流
    Integer[] arr = {1,3,5,7,9};
    Stream<Integer> stream1 = Arrays.stream(arr);
    
    //map 可以转entryset 转流
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.2 中间操作

    中间的操作是对流中数据操作,方法返回的还是Stream 意味着后面还是可以接着中间操作的函数

    数据准备

        public static List<User> getUsers() {
            List<User> users = new ArrayList<>();
            users.add(new User(1, "孙菲菲", 4, "杨浦区"));
            users.add(new User(1, "孙菲菲", 4, "杨浦区"));
            users.add(new User(2, "熊大", 5, "青青草原"));
            users.add(new User(3, "熊二", 10, "青青草原"));
            return users;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1.filter

    数据过滤

    Stream<T> filter(Predicate<? super T> predicate);
    
    
    
        public static void filter() {
            List<User> users = getUsers();
            users.stream()
                    .filter(x -> x.getId() > 1)  //中间是匿名函数 Predicate(结合2.3) 的实现 是一个判断条件
                    .forEach(x -> System.out.println(x));
        }
    
    //输出
    User(id=2, name=熊大, age=5, address=青青草原)
    User(id=3, name=熊二, age=10, address=青青草原) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    2.map

    数据转换

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
        public static void map() {
            List<User> users = getUsers();
            users.stream()
                    .map(x -> {
                        return "银河系,地球村 :" + x.getAddress();
                    }) //中间是匿名函数 Function(结合2.3) 的实现 y有输入和输出, 输入是User 对象 输出是 String
                    .forEach(x -> System.out.println(x));
        }
    
    //输出
    银河系,地球村 :杨浦区
    银河系,地球村 :杨浦区
    银河系,地球村 :青青草原
    银河系,地球村 :青青草原
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    3.distinct

    数据去重

     Stream<T> distinct();  
    
    
    public static void distinct() {
            List<User> users = getUsers();
            users.stream()
                    .distinct() //依赖于Object 的equals 方法来实现
                    .forEach(x -> System.out.println(x));
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    4.sorted

    排序

    Stream<T> sorted();  
    Stream<T> sorted(Comparator<? super T> comparator);
    
    public static void sorted() {
            List<User> users = getUsers();
            users.stream()
                    .sorted()
                    .forEach(x -> System.out.println(x));
        }
    
    //报错
    Exception in thread "main" java.lang.ClassCastException: com.common.util.User cannot be cast to java.lang.Comparable
      
    两个方法
      方法1:User 实现 Comparable 接口
      方法2:sorted(Comparator<? super T> comparator) 使用带参数的
    
    方法二实现
    public static void sorted() {
            List<User> users = getUsers();
            users.stream()
                    .sorted(Comparator.comparingInt(User::getAge))  //sorted() 使用带参数的
                    .forEach(x -> System.out.println(x));
    }
    
    • 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.limit

    截取前面几条数据

     Stream<T> limit(long maxSize);
    
    
        public static void limit() {
            List<User> users = getUsers();
            users.stream()
                    .limit(3)
                    .forEach(x -> System.out.println(x));
        }
    
    //输出
    User(id=1, name=孙菲菲, age=4, address=杨浦区)
    User(id=1, name=孙菲菲, age=4, address=杨浦区)
    User(id=2, name=熊大, age=5, address=青青草原)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    6.skip

    跳过前面几条数据

    Stream<T> skip(long n);
    
        public static void skip() {
            List<User> users = getUsers();
            users.stream()
                    .skip(2)
                    .forEach(x -> System.out.println(x));
        }
    
    User(id=2, name=熊大, age=5, address=青青草原)
    User(id=3, name=熊二, age=10, address=青青草原)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    7.flatMap

    map 对象是一对一转出 , flatMap 可以一对多转出对象

    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
      
      
      
        public static void flatMap() {
            List<User> users = getUsers();
            users.stream()
                    .flatMap((Function<User, Stream<?>>) user -> Arrays.asList(user.getId(), user.getAddress()).stream()) //flatMap 内是一个Function ,Function 的输出是一个 子流(可以这样理解,每个元素拆分的流)
                    .forEach(x -> System.out.println(x));
        }
    
    //输出
    1
    杨浦区
    1
    杨浦区
    2
    青青草原
    3
    青青草原
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.3 流的终结操作

    1.foreach

    遍历流中的数据

        public static void foreach() {
            List<User> users = getUsers();
            users.stream()
                    .forEach(x -> System.out.println(x));
        }
    
    //输出
    User(id=1, name=孙菲菲, age=4, address=杨浦区)
    User(id=1, name=孙菲菲, age=4, address=杨浦区)
    User(id=2, name=熊大, age=5, address=青青草原)
    User(id=3, name=熊二, age=10, address=青青草原)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.count

    计算流中的数据

        public static void count() {
            List<User> users = getUsers();
            long count = users.stream()
                    .filter(x -> x.getId() > 1)
                    .count();
            System.out.println(count);
        }
    
    //输出
    2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    3.max&min

    计算流中最大值,最小值

    public static void max() {
        List<User> users = getUsers();
        Optional<User> max = users.stream()
                .filter(x -> x.getId() > 1)
                .max(Comparator.comparingInt(User::getAge));//.min(Comparator.comparingInt(User::getAge));
        System.out.println(max.get()); //getAge 最大的
    }
    
    User(id=3, name=熊二, age=10, address=青青草原)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    4.collect

    流中的元素转变成集合

        public static void collect() {
            List<User> users = getUsers();
            List<Integer> list = users.stream()
                    .map(x -> x.getId())
                    .collect(Collectors.toList());
            System.out.println(list);
            System.out.println("=========");
    
            Set<Integer> set = users.stream()
                    .map(x -> x.getId())
                    .collect(Collectors.toSet());
            System.out.println(set);
            System.out.println("=========");
    
            Map<String, User> nameToUser = users.stream()
                    .collect(Collectors.toMap(User::getName, Function.identity()));
            System.out.println(nameToUser);
            System.out.println("========="); 
          //这里会报错 Exception in thread "main" java.lang.IllegalStateException: Duplicate key User(id=1, name=孙菲菲, age=4, address=杨浦区)
          //需要先去重 或者下面方法 重复使用哪一个
          //(x, x1) -> x 使用前一个
          //(x, x1) -> x1 使用后一个
          
    
            Map<String, User> nameToUser = users.stream()
                    .collect(Collectors.toMap(User::getName, Function.identity(), (x, x1) -> x1));
            System.out.println(nameToUser);
            System.out.println("=========");
    
            Map<String, List<User>> nameToUserList = users.stream()
                    .collect(Collectors.groupingBy(User::getName));
            System.out.println(nameToUserList);
            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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    5.查询匹配
    • anyMatch:是否有任意一个满足
    • allMatch:是否所有元素都满足
    • noneMatch: 是否都不满住
        public static void matchAndSelect() {
            List<User> users = getUsers();
            boolean b = users.stream().map(x -> x.getAge()).allMatch(x -> x > 10);
            System.out.println("所有age>10:" + b);
            b = users.stream().map(x -> x.getAge()).anyMatch(x -> x > 5);
            System.out.println("任意age>5:" + b);
            b = users.stream().map(x -> x.getAge()).noneMatch(x -> x > 10);
            System.out.println("没有一个age > 10:" + b);
        }
    //元素
            users.add(new User(1, "孙菲菲", 4, "杨浦区"));
            users.add(new User(1, "孙菲菲", 4, "杨浦区"));
            users.add(new User(2, "熊大", 5, "青青草原"));
            users.add(new User(3, "熊二", 10, "青青草原"));
    
    所有age>10:false
    任意age>5:true
    所有都<=10:true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • findAny :或取流中任意一个;(并行流中,任意一个流的第一个,串行流都是一样)
    • findFirst:获取流中第一个
        public static void select() {
            List<User> users = getUsers();
            Optional<User> any = users.stream().findAny();
            System.out.println(any);
    
            Optional<User> first = users.stream().findFirst();
            System.out.println(first);
        }
    
    //输出
    Optional[User(id=1, name=孙菲菲, age=4, address=杨浦区)]
    Optional[User(id=1, name=孙菲菲, age=4, address=杨浦区)]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    6.reduce

    对流中数据进行指定计算方式运行出结果( 聚合,缩减)

    比如说求和

        public static void reduce() {
            List<User> users = getUsers();
            Integer reduce = users.stream()
                    .map(x -> x.getAge())
                    .reduce(0, (integer, integer2) -> integer + integer2);
            System.out.println(reduce);
        }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.4 并行流

    大量数据的时候,可以使用并行流处理数据.

    并行流的底层是把任务分配给多个线程(ForkJoinn Pool 线程池)去执行.

        public static void parallelStream() {
            List<User> users = getUsers();
            Integer reduce = users.parallelStream()
                    .peek(x->{
                        System.out.println(Thread.currentThread().getName());
                    })
                    .map(x -> x.getAge())
                    .reduce(0, (integer, integer2) -> integer + integer2);
            System.out.println(reduce);
        }
    
    //输出
    ForkJoinPool.commonPool-worker-9
    ForkJoinPool.commonPool-worker-2
    ForkJoinPool.commonPool-worker-11
    main
    23
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.5 注意

    • 流是一次性的 使用过(被终结操作终结过的 就不能在使用了)
    • 流的操作一般不会影响原来数据地址(集合元素的地址)
  • 相关阅读:
    人脸自动贴国旗
    SpringBoot整合微信扫码登录
    R语言主成分分析可视化(颜值高,很详细)
    Verilog刷题[hdlbits] :Always if
    mac下配置环境-node以及nvm
    消息队列(MQ)面试
    设计模式学习笔记 - 桥接模式
    C语言每日一题
    docker 其他命令(镜像和容器常用命令外的命令)
    CentOS7.9安装elasticsearch-8.3.1和window 10安装elasticsearch-8.3.1
  • 原文地址:https://blog.csdn.net/weixin_44244088/article/details/128029630