程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java Lambda 表達式詳解及示例代碼

Java Lambda 表達式詳解及示例代碼

編輯:關於JAVA

Java Lambda 表達式詳解及示例代碼。本站提示廣大學習愛好者:(Java Lambda 表達式詳解及示例代碼)文章只能為提供參考,不一定能成為您想要的結果。以下是Java Lambda 表達式詳解及示例代碼正文


Java Lambda 表達式是 Java 8 引入的一個新的功效,可以說是模仿函數式編程的一個語法糖,相似於 Javascript 中的閉包,但又有些分歧,重要目標是供給一個函數化的語法來簡化我們的編碼。

Lambda 根本語法

Lambda 的根本構造為 (arguments) -> body,有以下幾種情形:

  1. 參數類型可推導時,不須要指定類型,如 (a) -> System.out.println(a)
  2. 當只要一個參數且類型可推導時,不強迫寫 (), 如 a -> System.out.println(a)
  3. 參數指定類型時,必需有括號,如 (int a) -> System.out.println(a)
  4. 參數可認為空,如 () -> System.out.println(“hello”)
  5. body 須要用 {} 包括語句,當只要一條語句時 {} 可省略

    罕見的寫法以下:

    (a) -> a * a
    (int a, int b) -> a + b
    (a, b) -> {return a - b;}
    () -> System.out.println(Thread.currentThread().getId())

    函數式接口 FunctionalInterface

    概念

    Java Lambda 表達式以函數式接口為基本。甚麼是函數式接口(FunctionalInterface)? 簡略說來就是只要一個辦法(函數)的接口,這類接口的目標是為了一個單一的操作,也就相當於一個單一的函數了。罕見的接口如:Runnable, Comparator 都是函數式接口,而且都標注了注解 @FunctionalInterface 。

    舉例

    以 Thread 為例解釋很輕易懂得。Runnable 接口是我們線程編程經常用的一個接口,就包括一個辦法 void run(),這個辦法就是線程的運轉邏輯。依照之前的語法,我們新建線程普通要用到 Runnable 的匿名類,以下:

    new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println(Thread.currentThread().getId());
      }
    
    }).start();
    
    

    假如寫多了,是否是很無聊,而基於 Lambda 的寫軌則變得簡練清楚明了,以下:

    new Thread(() -> System.out.println(Thread.currentThread().getId())).start();

    留意 Thread 的參數,Runnable 的匿名完成就經由過程一句就完成了出來,寫成上面的更好懂得

    Runnable r = () -> System.out.println(Thread.currentThread().getId());
    new Thread(r).start();

    固然 Lambda 的目標不只僅是寫起來簡練,更高條理的目標等領會到了再總結。

    再看一個比擬器的例子,依照傳統的寫法,以下:

    Integer[] a = {1, 8, 3, 9, 2, 0, 5};
    Arrays.sort(a, new Comparator<Integer>() {
      @Override
      public int compare(Integer o1, Integer o2) {
        return o1 - o2;
      }
    });
    

    Lambda 表達式寫法以下:

    Integer[] a = {1, 8, 3, 9, 2, 0, 5};
    Arrays.sort(a, (o1, o2) -> o1 - o2);

    JDK中的函數式接口

    為了現有的類庫可以或許直接應用 Lambda 表達式,Java 8 之前存在一些接口曾經被標注為函數式接口的:

    1. java.lang.Runnable
    2. java.util.Comparator
    3. java.util.concurrent.Callable
    4. java.io.FileFilter
    5. java.security.PrivilegedAction
    6. java.beans.PropertyChangeListener
    7. Java 8 中更是新增長了一個包 java.util.function,帶來了經常使用的函數式接口:

      1. Function<T, R> - 函數:輸出 T 輸入 R
      2. BiFunction<T, U, R> - 函數:輸出 T 和 U 輸入 R 對象
      3. Predicate<T> - 斷言/斷定:輸出 T 輸入 boolean
      4. BiPredicate<T, U> - 斷言/斷定:輸出 T 和 U 輸入 boolean
      5. Supplier<T> - 臨盆者:無輸出,輸入 T
      6. Consumer<T> - 花費者:輸出 T,無輸入
      7. BiConsumer<T, U> - 花費者:輸出 T 和 U 無輸入
      8. UnaryOperator<T> - 單位運算:輸出 T 輸入 T
      9. BinaryOperator<T> - 二元運算:輸出 T 和 T 輸入 T
      10. 別的還對根本類型的處置增長了加倍詳細的函數是接口,包含:BooleanSupplier, DoubleBinaryOperator, DoubleConsumer, DoubleFunction<R>, DoublePredicate, DoubleSupplier, DoubleToIntFunction, DoubleToLongFunction, DoubleUnaryOperator, IntBinaryOperator, IntConsumer, IntFunction<R>, IntPredicate, IntSupplier, IntToDoubleFunction, IntToLongFunction, IntUnaryOperator, LongBinaryOperator, LongConsumer,LongFunction<R>, LongPredicate, LongSupplier, LongToDoubleFunction,LongToIntFunction, LongUnaryOperator, ToDoubleBiFunction<T, U>, ToDoubleFunction<T>,ToIntBiFunction<T, U>, ToIntFunction<T>, ToLongBiFunction<T, U>, ToLongFunction<T> 。聯合下面的函數式接口,對這些根本類型的函數式接口經由過程類名就可以一眼看出接口的感化。

        創立函數式接口

        有時刻我們須要本身完成一個函數式接口,做法也很簡略,起首你要包管此接口只能有一個函數操作,然後在接口類型上標注注解 @FunctionalInterface 便可。

        類型推導

        類型推導是 Lambda 表達式的基本,類型推導的進程就是 Lambda 表達式的編譯進程。以上面的代碼為例:

        Function<String, Integer> strToInt = str -> Integer.parseInt(str);
        編譯時代,我懂得的類型推導的進程以下:

        1. 先肯定目的類型 Function
        2. Function 作為函數式接口,其辦法簽名為:Integer apply(String t)
        3. 檢測 str -> Integer.parseInt(str) 能否與辦法簽名婚配(辦法的參數類型、個數、次序 和前往值類型)
        4. 假如不婚配,則報編譯毛病
        5. 這裡的目的類型是症結,經由過程目的類型獲得辦法簽名,然後和 Lambda 表達式做出比較。

          辦法援用

          辦法援用(Method Reference)的基本異樣是函數式接口,可以直接作為函數式接口的完成,與 Lambda 表達式有雷同的感化,異樣依附於類型推導。辦法援用可以看做是只挪用一個辦法的 Lambda 表達式的簡化。

          辦法援用的語法為: Type::methodName 或許 instanceName::methodName , 結構函數對應的 methodName 為 new。

          例如下面曾用到例子:

          Function<String, Integer> strToInt = str -> Integer.parseInt(str);

          對應的辦法援用的寫法為

          Function<String, Integer> strToInt = Integer::parseInt;

          依據辦法的類型,辦法援用重要分為一下幾品種型,結構辦法援用、靜態辦法援用、實例上實例辦法援用、類型上實例辦法援用等

          結構辦法援用

          語法為: Type::new 。 以下面的函數為了將字符串轉為數組

          辦法援用寫法

          Function<String, Integer> strToInt = Integer::new;

          Lambda 寫法

          Function<String, Integer> strToInt = str -> new Integer(str);

          傳統寫法

          Function<String, Integer> strToInt = new Function<String, Integer>() {
            @Override
            public Integer apply(String str) {
              return new Integer(str);
            }
          };


          數組結構辦法援用

          語法為: Type[]::new 。以下面的函數為了結構一個指定長度的字符串數組

          辦法援用寫法

          Function<Integer, String[]> fixedArray = String[]::new;

          辦法援用寫法

          Function<Integer, String[]> fixedArray = length -> new String[length];

          傳統寫法

          Function<Integer, String[]> fixedArray = new Function<Integer, String[]>() {
            @Override
            public String[] apply(Integer length) {
              return new String[length];
            }
          };
          

          靜態辦法援用

          語法為: Type::new 。 以下面的函數異樣為了將字符串轉為數組

          辦法援用寫法

          Function<String, Integer> strToInt = Integer::parseInt;

          Lambda 寫法

          Function<String, Integer> strToInt = str -> Integer.parseInt(str);

          傳統寫法

          Function<String, Integer> strToInt = new Function<String, Integer>() {
            @Override
            public Integer apply(String str) {
              return Integer.parseInt(str);
            }
          };
          

          實例上實例辦法援用

          語法為: instanceName::methodName 。以下面的斷定函數用來斷定給定的姓名能否在列表中存在

          List<String> names = Arrays.asList(new String[]{"張三", "李四", "王五"});
          Predicate<String> checkNameExists = names::contains;
          System.out.println(checkNameExists.test("張三"));
          System.out.println(checkNameExists.test("張四"));

          類型上實例辦法援用

          語法為: Type::methodName 。運轉時援用是指高低文中的對象,以下面的函數來前往字符串的長度

          Function<String, Integer> calcStrLength = String::length;
          System.out.println(calcStrLength.apply("張三"));
          List<String> names = Arrays.asList(new String[]{"zhangsan", "lisi", "wangwu"});
          names.stream().map(String::length).forEach(System.out::println);

          又好比上面的函數已指定的分隔符朋分字符串為數組

          BiFunction<String, String, String[]> split = String::split;
          String[] names = split.apply("zhangsan,lisi,wangwu", ",");
          System.out.println(Arrays.toString(names));

          Stream 對象

          概念

          甚麼是 Stream ? 這裡的 Stream 分歧於 io 中的 InputStream 和 OutputStream,Stream 位於包 java.util.stream 中, 也是 java 8 新參加的,Stream 只的是一組支撐串行並行聚合操作的元素,可以懂得為聚集或許迭代器的加強版。甚麼是聚合操作?簡略舉例來講罕見的有均勻值、最年夜值、最小值、總和、排序、過濾等。

          Stream 的幾個特點:

          單次處置。一次處置停止後,以後Stream就封閉了。
          支撐並行操作
          罕見的獲得 Stream 的方法

          從聚集中獲得

          Collection.stream();
          Collection.parallelStream();

          靜態工場

          Arrays.stream(array)
          Stream.of(T …)
          IntStream.range()
          這裡只對 Stream 做簡略的引見,上面會有詳細的運用。要說 Stream 與 Lambda 表達式有甚麼關系,其實並沒有甚麼特殊慎密的關系,只是 Lambda 表達式極年夜的便利了 Stream 的應用。假如沒有 Lambda 表達式,應用 Stream 的進程中會發生年夜量的匿名類,異常別扭。

          舉例

          以下的demo依附於 Employee 對象,和由 Employee 對象構成的 List 對象。

          public class Employee {
          
            private String name;
            private String sex;
            private int age;
          
            public Employee(String name, String sex, int age) {
              super();
              this.name = name;
              this.sex = sex;
              this.age = age;
            }
            public String getName() {
              return name;
            }
          
            public String getSex() {
              return sex;
            }
            public int getAge() {
              return age;
            }
            @Override
            public String toString() {
              StringBuilder builder = new StringBuilder();
              builder.append("Employee {name=").append(name).append(", sex=").append(sex).append(", age=").append(age)
                  .append("}");
              return builder.toString();
            } 
          }
          List<Employee> employees = new ArrayList<>();
          employees.add(new Employee("張三", "男", 25));
          employees.add(new Employee("李四", "女", 24));
          employees.add(new Employee("王五", "女", 23));
          employees.add(new Employee("周六", "男", 22));
          employees.add(new Employee("孫七", "女", 21));
          employees.add(new Employee("劉八", "男", 20));
          
          

          打印一切員工

          Collection 供給了 forEach 辦法,供我們逐一操作單個對象。

          employees.forEach(e -> System.out.println(e));
          或許
          employees.stream().forEach(e -> System.out.println(e));

          按年紀排序

          Collections.sort(employees, (e1, e2) -> e1.getAge() - e2.getAge());
          employees.forEach(e -> System.out.println(e));
          或許
          employees.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(e -> System.out.println(e));
          打印年紀最年夜的女員工

          max/min 前往指定排序前提下最年夜/最小的元素

          Employee maxAgeFemaleEmployee = employees.stream()
              .filter(e -> "女".equals(e.getSex()))
              .max((e1, e2) -> e1.getAge() - e2.getAge())
              .get();
          System.out.println(maxAgeFemaleEmployee);
          

          打印出年紀年夜於20 的男員工

          filter 可以過濾出相符前提的元素

          employees.stream()
                  .filter(e -> e.getAge() > 20 && "男".equals(e.getSex()))
                  .forEach(e -> System.out.println(e));
          打印出年紀最年夜的2名男員工

          limit 辦法截取無限的元素

          employees.stream()
              .filter(e -> "男".equals(e.getSex()))
              .sorted((e1, e2) -> e2.getAge() - e1.getAge())
              .limit(2)
              .forEach(e -> System.out.println(e));
          

          打印出一切男員工的姓名,應用 , 分隔

          map 將 Stream 中一切元素的履行給定的函數後前往值構成新的 Stream

          String maleEmployeesNames = employees.stream()
              .map(e -> e.getName())
              .collect(Collectors.joining(","));
          System.out.println(maleEmployeesNames);
          

          統計信息

          IntSummaryStatistics, DoubleSummaryStatistics, LongSummaryStatistics 包括了 Stream 中的匯總數據。

          IntSummaryStatistics stat = employees.stream()
              .mapToInt(Employee::getAge).summaryStatistics();
          System.out.println("員工總數:" + stat.getCount());
          System.out.println("最高年紀:" + stat.getMax());
          System.out.println("最大年齡:" + stat.getMin());
          System.out.println("均勻年紀:" + stat.getAverage());
          

          總結

          Lambda 表達式確切可以削減許多代碼,能進步臨盆力,固然也有弊病,就是龐雜的表達式可讀性會比擬差,也能夠是還不是很習氣的原因吧,假如習氣了,信任會愛好上的。凡事都有兩面性,就看我們若何去均衡這個中的利害了,特別是在一個團隊中。

          以上就是對Java8 JavaLambda 的材料整頓,後續持續彌補相干材料感謝年夜家對本站的支撐!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved