Java8 Lambda表达式

Java8 Lambda表达式


  • 前言
    编程的模式有面向对象,面向过程,面向函数
    对函数编程语言的支持目前有Scala、Erlang、F#、Python、Php、JavaJavaScript

  • 好处:

    1. 使代码更加紧凑
      java7写法:

      new Thread(new Runnable() {
          @Override
          public void run() {
              System.out.println("java7写法"+" 当前线程是 "+Thread.currentThread().getName());
          }
      }).start();

      java8 Lambda表达式写法:

      new Thread(() ->System.out.println("java8 lambda表达式 "+" 当前线程是 "+Thread.currentThread().getName())).start();

      Console 输出结果如下:

      java7写法 当前线程是 Thread-0
      java8 lambda表达式  当前线程是 Thread-1
    2. 方法的参数可以是一个函数接口单元。

      java7写法:

      public static void main(String[] args) {
        String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
        Arrays.sort(s,new Comparator() {
          @Override
          public int compare(String o1, String o2) {
              return Integer.compare(o1.length(), o2.length());
          }
        });
      }

      java8 Lambda表达式写法:

      public static void main(String[] args) {
        String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
      
        /**
         * 利用Arrays(工具类).sort(String[] a, Comparator c)
         *  
         * 将String类型数组中的元素按长度进行排序。
         * 
         */
        Arrays.sort(s, (param1,param2) ->Integer.compare(param1.length(), param2.length()));;
      
        /**
         * 利用StreamAPI#forEach()来输出排序后数组元素。
         */
        Stream.of(s).forEach(parameter ->System.out.println(parameter));
      }

    由上面可知,使用Lambda表达式可以改变一个函数接口的行为,而不需要去实现Comparable接口。

  • Syntax(句法):

    ( Parameters参数1 ,参数2,... ) ->{ expressions语句块1; 语句块2 ; (return)...;}

    注意点:

    1. 当只有一个参数时,大括号可以省略
    2. 当一个语句块,花括号可以省略
    3. 当有返回值,且多个语句块时,返回值的语句块需添加return.

    这和JavaScript中ES6语法中箭头函数类似的,都是面向方法的编程,在前言也提到了。

  • 方法的引用:

    若是需执行的代码在某些类中已经存在,则不需要去写Lambda表达式,可以直接引用该方法。这叫做方法引用。

    未引用前的代码:

    Stream.of(datas).forEach(param -> {System.out.println(param);});

    使用方法引用了的代码:

    /**
      * 对一个类中已经存在的方法的引用。方法引用,这里是class::staticMethod(类::静态方法)
      */
    Stream.of(s).forEach(System.out::println);

    方法引用的具体分类:

    Object:instanceMethod
    Class:staticMethod
    Class:instanceMethod

    解释:
    第一种和第二种差不多,都是将参数传递给方法。

    System.out::println 等同于 x -> System.out.println(x)

    第三种是第一参数是方法执行的目标

     /**
       * 对一个类已经存在的方法的引用。方法引用,这里是class::instanceMethod(类::对象调用的方法)
       * String s1,s2;
       * s.compareTo(s2);
       */
        Arrays.sort(s, String:: compareTo);
    String::compareToIgnoreCase 等同于 (x,y) ->     x.compareToIgnoreCase(y)
  • 构造方法引用:

    构造方法引用与方法引用不同,构造方法引用的方法是new.
    例子:

    String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
    Stream<String> stream=Stream.of(s);
    String[] s1=stream.toArray(String[] :: new);

    构建方法引用有以下两种形式:

    Class::new
    Class[]::new
  • Lambda表达式作用域:

    Lanmbda表达式的变量作用域与内部类类似,但是放宽了内部类引用外部类的变量的条件。

    在java7中内部类引用外部类的变量,案例如下:

    final   String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
    
    new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(s[0]);
    }
    }).start();

    在java8Lambda表达式中,写法如下:

    String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
    
    new Thread(() -> System.out.println([0])).start();
    

    Console控制台中输出结果如下:

    Java8 Lambda表达式_第1张图片

    可发现,java8中放款了对引用条件,不再强制要求加上final关键字。
    但是,java8要求这个变量是effectively final。

    Effectively final:有效的只读变量,一旦定义后,在后面中不能随意修改的。

    这里写图片描述

    注意点:Java中内部类以及Lambda表达式中也不允许修改外部类中的变量,这是为了避免多线程情况下的race condition

    Java8 Lambda表达式_第2张图片

  • Lambda表达式可用的变量:

    Lambda表达式可以访问传递给它的变量,可以访问它自己内部定义的变量,也可以访问外部的变量。

    注意点: Lambda表达式访问的外部变量有一个限制,变量的引用地址不可变。这和上面提到effectively final的变量是同一个道理。

  • Lambda表达式中this:

    在Lambda表达式中,this不是指向的Lambda所产生的那个对象,而是声明它的外部对象。

    public class Write {
    public static void main(String[] args) {
       new Write().lambdaTest();
    }
    public void lambdaTest(){
      String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
      Arrays.asList(s)
            .stream()
            .map((str)->{
                System.out.println(" this指向对象的名字: "+this.getName());
               return str.toUpperCase();
                })
            .collect(Collectors.toList())
            .forEach(System.out::println);
    }
    public String getName(){
      return Write.class.getSimpleName();
    }
    }

    Console控制台输出信息:

    
    this指向对象的名字: Write
    this指向对象的名字: Write
    this指向对象的名字: Write
    
    红太狼MOTHER
    灰太狼FATHER
    小灰灰SON

Stream API特性


  • 理解(引用来源):

    1.Stream是元素的集合,这点让Stream看起来用些类似Iterator;
    2.可以支持顺序和并行的对原Stream进行汇聚的操作;

    大家可以把Stream当成一个装饰后的Iterator。原始版本的Iterator,用户只能逐个遍历元素并对其执行某些操作;包装后的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,具体这些操作如何应用到每个元素上,就给Stream就好了!原先是人告诉计算机一步一步怎么做,现在是告诉计算机做什么,计算机自己决定怎么做。当然这个“怎么做”还是比较弱的

  • Stream使用介绍:

    基本使用步骤:

    • 创建Stream对象;
    • 转换Stream对象,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换);
    • 对Stream进行聚合(Reduce)操作,最终获取想要的结果;
  • 创建Stream对象:

    1. Stream中静态方法:
      Stream.of()方法创建

      //参数是一个对象(数组或者类对象)
      Stream.of(new String[]{"新根"}); 

      Stream.generate()方法创建:

      //参数是一个Supplier接口对象,该接口用于创建给定类型的对象。
      Stream.generate(() -> Math.random());

      Stream.iterate(seed, f)方法创建:

      Stream.iterate("新", (params)->{ return params+"根";}).limit(1);

      注意点:需使用limit()限制迭代次数,不然会无限循环下去。

    2. 通过Collection接口的默认方法:

      各种Collection接口,或者其子类都具备stream()

      String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
      Arrays.asList(s)
          .stream();
  • Stream转换:

    1. distinct():去掉重复的元素,形成新的Stream.

    2. filter():对元素进行过滤,新的Stream只包含过滤后的元素

    3. map():对元素进行遍历转换,新的Stream只包含新转换的元素

    4. flatMap:对元素进行遍历,新的Stream包含新转换的元素和原本的元素

    5. peek():

    6. limit():获取前几个元素,形成新的Stream

    7. skip():跳过第几个元素开始形成新的Stream.

  • Stream Reduce(汇聚):

    1. 含义:汇聚操作(也称为折叠)接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果。

    2. 可变汇聚:
      2.1 collect方法是将Stream中要用到的元素全部收集到一个结果容器。

    3. 其他汇聚:

      3.1 reduce方法: 参考:博客教程2

      3.2 count方法:获取Stream中元素的个数

  • 代码分析:

    String [] s=new String[]{"红太狼mother","灰太狼father","小灰灰son"};
    Arrays.asList(s)
            .stream()
            .map((str)->{
                System.out.println(" this指向对象的名字: "+this.getName());
               return str.toUpperCase();
                })
            .collect(Collectors.toList())
            .forEach(System.out::println);

    从上面的代码可知:

    1. stream()创建了一个Stream对象。
    2. map()进行一个Stream对象的转换
    3. collect()进行reduce Stream
    4. 最后循环输出结果

资源参考:

  • 博客教程1: http://www.cnblogs.com/WJ5888/p/4618465.html

  • 博客教程2:http://www.cnblogs.com/aoeiuv/p/5911692.html

你可能感兴趣的:(编程基础(Java,Kotlin,SQL),Java,SE知识点)