lambda表達式10個示例——學習筆記

摘錄:http://www.importnew.com/16436.htmlhtml

一、lambda實現Runnable

複製代碼
// Java 8以前:
new Thread(new Runnable() { @Override public void run() {
    System.out.println("Before Java8, too much code for too little to do");
    }
}).start();
複製代碼
//Java 8方式:用() -> {}代碼塊替代了整個匿名類
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

output:java

too much code, for too little to do
Lambda expression rocks !!

若是你的方法不對參數進行修改、重寫,只是在控制檯打印點東西的話,那麼能夠這樣寫:git

() -> System.out.println("Hello Lambda Expressions");

若是你的方法接收兩個參數,那麼能夠寫成以下這樣:shell

若是你的方法接收兩個參數,那麼能夠寫成以下這樣:

二、使用Java 8 lambda表達式進行事件處理

編寫事件監聽:express

複製代碼
// Java 8以前:
JButton show =  new JButton("Show");
show.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) {
    System.out.println("Event handling without lambda expression is boring");
    }
});
複製代碼
// Java 8方式:
show.addActionListener((e) -> {
    System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});

output:編程

too much code, for too little to do
Lambda expression rocks !!

三、使用lambda表達式對列表進行迭代

// Java 8以前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
    System.out.println(feature);
}
複製代碼
// Java 8以後:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
 
// 使用Java 8的方法引用更方便,方法引用由::雙冒號操做符標示, // 看起來像C++的做用域解析運算符
features.forEach(System.out::println);
複製代碼

output:微信

Lambdas
Default Method
Stream API
Date and Time API

四、使用lambda表達式和函數式接口Predicate

 java.util.function.Predicate 函數式接口以及lambda表達式,能夠向API方法添加邏輯,用更少的代碼支持更多的動態行爲。網絡

複製代碼
public static void main(args[]){
    List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 
    System.out.println("Languages which starts with J :");
    filter(languages, (str)->str.startsWith("J"));
 
    System.out.println("Languages which ends with a ");
    filter(languages, (str)->str.endsWith("a"));
 
    System.out.println("Print all languages :");
    filter(languages, (str)->true);
 
    System.out.println("Print no language : ");
    filter(languages, (str)->false);
 
    System.out.println("Print language whose length greater than 4:");
    filter(languages, (str)->str.length() > 4);
}
 
public static void filter(List names, Predicate condition) {
    for(String name: names)  {
        if(condition.test(name)) {
            System.out.println(name + " ");
        }
    }
}
複製代碼

output:閉包

複製代碼
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
複製代碼

better way:app

// 更好的辦法
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

五、如何在lambda表達式中加入Predicate

java.util.function.Predicate 容許將兩個或更多的 Predicate 合成一個。它提供相似於邏輯操做符AND和OR的方法,名字叫作and()、or()和xor(),用於將傳入 filter() 方法的條件合併起來。

複製代碼
// 甚至能夠用and()、or()和xor()邏輯函數來合併Predicate,
// 例如要找到全部以J開始,長度爲四個字母的名字,你能夠合併兩個Predicate並傳入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
複製代碼

也可使用 or() 和 xor() 方法

六、Java 8中使用lambda表達式的Map和Reduce示例

將 costBeforeTax 列表的每一個元素轉換成爲稅後的值。咱們將 x -> x*x lambda表達式傳到 map() 方法,後者將其應用到流中的每個元素。而後用 forEach() 將列表元素打印出來。

複製代碼
// 不使用lambda表達式爲每一個訂單加上12%的稅
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    System.out.println(price);
}
 
// 使用lambda表達式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
複製代碼
複製代碼
112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0
複製代碼

6.二、Java 8中使用lambda表達式的Map和Reduce示例

Map和Reduce操做是函數式編程的核心操做,由於其功能,reduce 又被稱爲摺疊操做。

SQL中相似 sum()、avg() 或者 count() 的彙集函數,實際上就是 reduce 操做,由於它們接收多個值並返回一個值。

流API定義的 reduceh() 函數能夠接受lambda表達式,並對全部值進行合併。IntStream這樣的類有相似 average()、count()、sum() 的內建方法來作 reduce 操做,也有mapToLong()、mapToDouble() 方法來作轉換

複製代碼
// 爲每一個訂單加上12%的稅
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    total = total + price;
}
System.out.println("Total : " + total);
 
// 新方法:能夠用內建方法,也能夠本身定義
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
複製代碼

output:

Total : 1680.0
Total : 1680.0

七、經過過濾建立一個String列表

// 建立一個字符串列表,每一個字符串長度大於2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);

output:

Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]

八、對列表的每一個元素應用函數

// 將字符串換成大寫並用逗號連接起來
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);

九、複製不一樣的值,建立一個子列表

// 用全部不一樣的數字建立一個正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);

output:

Original List : [9, 10, 3, 4, 7, 3, 4],  Square Without duplicates : [81, 100, 9, 16, 49]

十、計算集合元素的最大值、最小值、總和以及平均值

ntStream、LongStream 和 DoubleStream 等流的類中,有個很是有用的方法叫作 summaryStatistics() 。能夠返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各類摘要數據。

最大值  最小值  全部元素的總和  平均值

複製代碼
//獲取數字的個數、最小值、最大值、總和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
複製代碼

輸出:

Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9

Lambda表達式 vs 匿名類

既然lambda表達式即將正式取代Java代碼中的匿名內部類,那麼有必要對兩者作一個比較分析。

一個關鍵的不一樣點就是關鍵字 this:

匿名類的 this 關鍵字指向匿名類,而lambda表達式的 this 關鍵字指向包圍lambda表達式的類。

另外一個不一樣點是兩者的編譯方式:

Java編譯器將lambda表達式編譯成類的私有方法。使用了Java 7的 invokedynamic 字節碼指令來動態綁定這個方法

注意:

1)lambda表達式僅能放入以下代碼:預約義使用了 @Functional 註釋的函數式接口,自帶一個抽象函數的方法,或者SAM(Single Abstract Method 單個抽象方法)類型。這些稱爲lambda表達式的目標類型,能夠用做返回類型,或lambda目標代碼的參數。例如,若一個方法接收Runnable、Comparable或者 Callable 接口,都有單個抽象方法,能夠傳入lambda表達式。相似的,若是一個方法接受聲明於 java.util.function 包內的接口,例如 Predicate、Function、Consumer 或 Supplier,那麼能夠向其傳lambda表達式。

2)lambda表達式內可使用方法引用,僅當該方法不修改lambda表達式提供的參數。本例中的lambda表達式能夠換爲方法引用,由於這僅是一個參數相同的簡單方法調用。

list.forEach(n -> System.out.println(n));
list.forEach(System.out::println);  // 使用方法引用
 

然而,若對參數有任何修改,則不能使用方法引用,而需鍵入完整地lambda表達式,以下所示:

list.forEach((String s) -> System.out.println("*" + s + "*"));

事實上,能夠省略這裏的lambda參數的類型聲明,編譯器能夠從列表的類屬性推測出來。

3)lambda內部可使用靜態、非靜態和局部變量,這稱爲lambda內的變量捕獲。

4)Lambda表達式在Java中又稱爲閉包或匿名函數,因此若是有同事把它叫閉包的時候,不用驚訝。

5)Lambda方法在編譯器內部被翻譯成私有方法,並派發 invokedynamic 字節碼指令來進行調用。可使用JDK中的 javap 工具來反編譯class文件。使用 javap -p 或 javap -c -v 命令來看一看lambda表達式生成的字節碼。大體應該長這樣:

private static java.lang.Object lambda$0(java.lang.String);

6)lambda表達式有個限制,那就是隻能引用 final 或 final 局部變量,這就是說不能在lambda內部修改定義在域外的變量。

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });
Compile time error : "local variables referenced from a lambda expression must be final or effectively final"
 

另外,只是訪問它而不做修改是能夠的,以下所示:

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); })

輸出:

4
6
10
14

 

分類: JDK8新特性
標籤: JDK8, Java
0
0
« 上一篇: 對象在內存中初始化的過程?
» 下一篇: 第1張 Maven簡介 學習筆記

Java 8 Lambda表達式和理解

說明:部分資料來源於網絡

時間:20190704

Lambda 表達式,也可稱爲閉包,它是推進 Java 8 發佈的最重要新特性。Lambda 容許把函數做爲一個方法的參數(函數做爲參數傳遞進方法中)。使用 Lambda 表達式可使代碼變的更加簡潔緊湊。

 

1、語法

一、lambda 表達式的語法格式以下:

  (左邊)輸入參數->(右邊)lambda主體

(parameters) -> expression;

  或

(parameters) ->{ statements; }

 

二、如下是lambda表達式的重要特徵:

(1)輸入參數:

  可選類型聲明:不須要聲明參數類型,編譯器能夠統一識別參數值。

  可選的參數圓括號:

    a、一個參數無需定義圓括號,但多個參數須要定義圓括號。

    b、若是申明瞭參數類型,則必定須要圓括號。

(2)lambda主體

  可選的大括號:若是主體包含了一個語句,就不須要使用大括號。

  可選的返回關鍵字:若是主體只有一個表達式返回值則編譯器會自動返回值,大括號須要指定明表達式返回了一個數值

 

三、按照上面的格式,lambda不一樣參數的表達式寫法

(1)沒有參數的表達式:

() -> System.out.println("this is no parameter Lambda expression");

(2)只有一個參數的表達式:

(x) -> System.out.println("this is only one parameter Lambda expression");

  或者

(X x) -> System.out.println("this is only one parameter Lambda expression");

  和

(x) -> {
    x = x*2;
    System.out.println("this is only one parameter Lambda expression");
    System.out.println("the function is double input value");
    return x;
};

  說明:一個參數的能夠不用使用(),若是參數聲明瞭參數類型則必需要加()Lambda主體是語句塊的話須要使用{}

(3)有兩個或者多個參數的表達式:

(x,y) -> System.out.println("these are several parameters Lambda expression");

  或者

(X x,Y y) -> {
  System.out.println("the function is add two input values");
  System.out.println("these are several parameters Lambda expression");
  return x+y;
};

  說明:有多個參數的lambda表達式,參數使用,隔開。Lambda主體是語句塊的話須要使用{}

 

2、Lambda 表達式實例

lambda示例1:

複製代碼
/*
 * Predicate<T>接口中boolean test(T t)方法只接收一個參數,返回值爲boolean類型,
 * 故lambda表達式的輸入參數只有一個參數:x,lambda的主體返回值爲boolean類型
 */
Predicate<Integer> atLeast5 = x -> x >= 5;
System.out.println("傳入參數是否大於5:" + atLeast5.test(6));

/*
 * BinaryOperator<T>接口中R apply(T t, U u)方法接收兩個泛型參數,返回值爲也爲泛型
 * 故lambda表達式輸入Long參數有兩個:x,y,lambda的主體返回值爲Long類型
 */
BinaryOperator<Long> addLongs = (x, y) -> {
  Long z = x + y;
  return z;
};
System.out.println("計算傳入兩個Long參數的和:" + addLongs.apply(5l, 6l));

/*
 * Runnable接口中run()方法沒有參數,有沒有返回值,故lambda表達式沒有參數,lambda主體也沒有返回值
 */
Runnable run1 = () -> System.out.println("這個方法就是run裏面的方法");
run1.run();

try {
  final Integer value = 9;
  /*
   *  一、Callable<V>接口中V call() throws Exception方法沒有參數,故lambda表達式也不能有輸入參數,
   * 返回類型爲泛型,故lambda主體須要返回指定Integer類型
   *  二、由於.call()方法拋出異常,因此須要拋出或者捕獲異常
   *  三、lambda表達式使用外部參數,須要是final類型,及時外部參數沒有定義爲final類型,
   * 也會隱式的指定爲final,故值或者引用地址不能修改。
   */
  Callable<Integer> call = () -> value;
  System.out.println("無參的方法,因此lambda的參數列表中不能傳遞參數:" + call.call());
} catch (Exception e) {
  e.printStackTrace();
}
複製代碼

lambda示例二:

複製代碼
public class Java8Tester { 

  public static void main(String args[]){ 
    Java8Tester tester
= new Java8Tester();     // 類型聲明     MathOperation addition = (int a, int b) -> a + b;     // 不用類型聲明     MathOperation subtraction = (a, b) -> a - b;     // 大括號中的返回語句     MathOperation multiplication = (int a, int b) -> { return a * b; };     // 沒有大括號及返回語句     MathOperation division = (int a, int b) -> a / b;     System.out.println("10 + 5 = " + tester.operate(10, 5, addition));     System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));     System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));     System.out.println("10 / 5 = " + tester.operate(10, 5, division));     // 不用括號     GreetingService greetService1 = message -> System.out.println("Hello " + message);     // 用括號     GreetingService greetService2 = (message) -> System.out.println("Hello " + message);     greetService1.sayMessage("Runoob");     greetService2.sayMessage("Google");   }

  // 定義接口和接口中爲實現的方法   
interface MathOperation {
    int operation(int a, int b);
  }

  // 定義接口和接口中爲實現的方法
  interface GreetingService {
    void sayMessage(String message);
  }

  // 定義方法,方法經過lambda表達式實現了接口中的方法   
private int operate(int a, int b, MathOperation mathOperation){     return mathOperation.operation(a, b);   } }
複製代碼

執行以上代碼,輸出結果爲:

10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

使用 Lambda 表達式須要注意如下兩點:

Lambda表達式主要用來定義行內執行的方法類型接口,例如,一個簡單方法接口。在上面例子中,咱們使用各類類型的Lambda表達式來定義MathOperation接口的方法。而後咱們定義了sayMessage的執行。

Lambda 表達式免去了使用匿名方法的麻煩,而且給予Java簡單可是強大的函數化的編程能力。

Lambda表達式經常使用的場景是:函數式接口。函數式接口是指的只有一個抽象方法的接口,咱們經常使用的函數式接口有:

Runnable、Callable、PrivilegedAction、Comparator、FileFilter、PathMatcher、InvocationHandler、PropertyChangeListener、ActionListener、ChangeListener、Function、Predicate、BinaryOperator

 

3、lambda表達式中的變量做用域

lambda表達式只能引用標記了final的外層局部變量,這就是說不能在lambda內部修改定義在域外的局部變量,不然會編譯錯誤。

Java8Tester.java 文件輸入如下代碼:

複製代碼
public class Java8Tester {

  final static String salutation = "Hello! "; 

  public static void main(String args[]){
    GreetingService greetService1
= message -> System.out.println(salutation + message);     greetService1.sayMessage("Runoob");     //====================至關於下面==============================     GreetingService g = new GreetingService() {       @Override       public void sayMessage(String message) {         System.out.println(salutation + message);       }     };     g.sayMessage("jack");
  }   
interface GreetingService {     void sayMessage(String message);   } }
複製代碼

執行以上腳本,輸出結果爲:

$ javac Java8Tester.java 
$ java Java8Tester
Hello! Runoob
Hello! jack

咱們也能夠直接在 lambda 表達式中訪問外層的局部變量:

複製代碼
public class Java8Tester { 

  public static void main(String args[]) { 
    final int num = 1; 
    Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); 
    s.convert(2); // 輸出結果爲 3 
  } 

  public interface Converter<T1, T2> { 
    void convert(int i); 
  } 
}
複製代碼

lambda表達式的局部變量能夠不聲明爲final,可是不可被後面的代碼修改(即隱性的具備final 的語義)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  //報錯信息:Local variable num defined in an enclosing scope must be final or effectively final

Lambda 表達式當中不容許聲明一個與局部變量同名的參數或者局部變量。

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //編譯會出錯
相關文章
相關標籤/搜索