Java SE 8: Lambda Quick Start Part.IV (Fin)

前面的章節中介紹了函數式接口並完成了一個基本的lambda表達式語法示例. 本節回顧lambda表達式如何改善集合類.html

Lambda表達式和集合類

在前面的例子中, 集合類被屢次用到. 然而, 若干新的lambda表達式特性改變了它們的使用方法. 本節講介紹一部分這樣的新特性.java

附加類

司機, 飛行員, 役男的搜索條件被封裝到SearchCriteria類中web

package com.example.lambda;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;

/**
 * @author MikeW
 */
public class SearchCriteria {

    private final Map<String, Predicate<Person>> searchMap = new HashMap<>();

    private SearchCriteria() {
        super();
        initSearchMap();
    }

    public static SearchCriteria getInstance() {
        return new SearchCriteria();
    }

    private void initSearchMap() {
        Predicate<Person> allDrivers  = p -> p.getAge() >= 16;
        Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
        Predicate<Person> allPilots   = p -> p.getAge() >= 23 && p.getAge() <= 65;
        searchMap.put("allDrivers", allDrivers);
        searchMap.put("allDraftees", allDraftees);
        searchMap.put("allPilots", allPilots);
    }

    public Predicate<Person> getCriteria(String PredicateName) {
        Predicate<Person> target;
        target = searchMap.get(PredicateName);
        if (target == null) {
            System.out.println("Search Criteria not found... ");
            System.exit(1);
        }
        return target;
    }
}

基於Predicate的搜索條件存儲在此類中, 而且可用於咱們的測試方法.算法

循環

第一個要注意的特性是任何集合類均可以使用的新的forEach方法. 如下是打印Person列表的幾個示例.編程

package com.example.lambda;

import java.util.List;

/**
 * @author MikeW
 */
public class Test01ForEach {

    public static void main(String[] args) {
        List<Person> pl = Person.createShortList();
        System.out.println("\n=== Western Phone List ===");
        pl.forEach(p -> p.printWesternName());
        System.out.println("\n=== Eastern Phone List ===");
        pl.forEach(Person::printEasternName);
        System.out.println("\n=== Custom Phone List ===");
        pl.forEach(p -> {
            System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail()));
        });
    }
}

第一個例子展現了使用標準lambda語法調用printWesternName方法來打印列表中的每一個Person對象. 第二個例子演示了方法引用. 在已經存在對該類 (Person) 執行操做的方法 (printEasternName) 的狀況下, 可使用該語法而不是正常的lambda表達式語法. 最後一個例子顯示了在這種狀況下使用printCustom方法運行. 請注意, 當一個lambda表達式包含在另外一個lambda表達式中時,變量名稱稍有不一樣.api

全部的集合類都能用此方法迭代. 基本結構相似於加強型for循環. 可是. 在類中包括一個迭代機制提供了許多好處.oracle

連接和過濾器

除了循環集合的內容以外,您還能夠將方法連接在一塊兒。第一種方法是將Predicate接口做爲參數的過濾器。如下示例在首次過濾結果後循環列表。函數

package com.example.lambda;

import java.util.List;

/**
 * @author MikeW
 */
public class Test02Filter {
    public static void main(String[] args) {
        List<Person> pl = Person.createShortList();
        SearchCriteria search = SearchCriteria.getInstance();

        System.out.println("\n=== Western Pilot Phone List ===");
        pl.stream().filter(search.getCriteria("allPilots")).forEach(Person::printWesternName);

        System.out.println("\n=== Eastern Draftee Phone List ===");
        pl.stream().filter(search.getCriteria("allDraftees")).forEach(Person::printEasternName);
    }
}

這兩個循環演示瞭如何基於搜索條件篩選列表.測試

Getting Lazy

這些特性頗有用, 可是爲何當已經有很是完美的for循環了還把它們加入到集合類中呢? 經過將迭代特性移動到庫中,它容許Java的開發人員進行更多的代碼優化。爲了進一步說明,幾個術語須要定義。優化

  • Laziness: 在編程中,懶漢模式指的是隻處理須要處理的對象。在上面的例子中, 後一個循環就是"餓漢模式", 由於在列表被過濾後, 它只遍歷了兩個Person對象. 代碼會更加高效, 由於最終的處理步驟只處理被選出的對象.
  • Eagerness: 對列表中的每一個對象執行操做的代碼被認爲是「餓漢模式」. 例如,一個加強的for循環遍歷整個列表以處理兩個對象,被認爲是一種更「急切」的方法。

經過操做集合的循環, 當機會出現時,代碼能夠更好地針對「懶漢模式」進行優化. 而當餓漢模式更有意義時(好比, 求和或求平均), 仍然採用餓漢模式. 這種方法比老是使用渴望的操做更有效和靈活。

The stream Method

在前面的代碼示例中,請注意,在過濾和循環開始以前調用了stream方法. 此方法使用集合類做爲輸入,並返回java.util.stream.Stream接口做爲輸出。一個steam表明了一個其上能夠連接各類方法的元素序列. 默認狀況下,一旦元素被消費,它們就再也不可從stream中得到。 所以, 一個操做鏈只能在特定的stream上發生一次. 此外, stream能夠是串行(默認)或並行的,具體取決於調用的方法。本文的末尾包含有一個並行stream示例.

變動和結果

正如前述, stream在使用後被丟棄. 所以, 集合中的元素不能經過stream被更改或變動. 然而, 若是你想保存操做鏈從集合中返回的元素呢? 你能夠將他們保存到一個新的集合中. 下面的代碼將展現如何實現:

public class Test03toList {
  public static void main(String... args){
    List<Person> pl = Person.createShortList();
    SearchCriteria search = SearchCriteria.getInstance();
    // Make a new list after filtering.
    List<Person> pilotList = pl.stream().filter(search.getCriteria("allPilots")).collect(Collectors.toList());
    System.out.println("\n=== Western Pilot Phone List ===");
    pilotList.forEach(Person::printWesternName);
  }
}

collect方法只有一個參數, 就是Collectors類. Collectors類可以基於stream的結果返回一個List或者Set對象. 上面的例子展現了stream的結果是如何在遍歷完成時被分配到一個List對象中的.

用映射 (map) 方法計算

映射方法一般和過濾器 (filter) 一塊兒使用. 該方法從一個類接受一個屬性, 並作一些事情. 如下示例經過基於年齡執行計算來演示此操做.

public class Test04Map {
  public static void main(String[] args) {
    List<Person> pl = Person.createShortList();
    SearchCriteria search = SearchCriteria.getInstance();
    // Calc average age of pilots old style
    System.out.println("== Calc Old Style ==");
    int sum = 0;
    int count = 0;
    for (Person p:pl){
      if (p.getAge() >= 23 && p.getAge() <= 65 ){
        sum = sum + p.getAge();
        count++;
      }
    }
    long average = sum / count;
    System.out.println("Total Ages: " + sum);
    System.out.println("Average Age: " + average);
    // Get sum of ages
    System.out.println("\n== Calc New Style ==");
    long totalAge = pl.stream().filter(search.getCriteria("allPilots")).mapToInt(Person::getAge).sum();
    // Get average of ages
    OptionalDouble averageAge = pl.parallelStream().filter(search.getCriteria("allPilots")).mapToDouble(Person::getAge).average();
    System.out.println("Total Ages: " + totalAge);
    System.out.println("Average Age: " + averageAge.getAsDouble());    
  }
}

這個程序計算列表中全部飛行員的平均年齡. 第一個for循環展現了舊式算法. 第二個循環經過映射方法來取得串行流中每一個person對象的年齡.
注意: 第二次計算平均值時, 並不須要先求和. 不過, 仍是做爲一種啓發性的展現了sum方法.
最後一個循環從stream中計算出了平均年齡. 注意它使用了parallelStream方法來獲得一個並行流 (parallel stream), 從而能夠同時計算這些值. 它的返回值也有稍許不一樣(詳見optional api).

源代碼: LambdaCollectionExamples.zip

總結

經過本教程, 你學到了如何使用:

  • 匿名內部類
  • 經過Lambda表達式替換匿名內部類
  • Lambda表達式語法
  • 用於在列表上執行搜索的謂詞 (Predicate) 接口
  • 用於處理對象並生成不一樣類型的對象的函數接口
  • 在Java SE 8中的Collections類添加的支持lambda表達式的新功能

資源

有關Java SE 8和lambda表達式的更多信息,請參見:

致謝

  • Lead Curriculum Developer: Michael Williams
  • QA: Juan Quesada Nunez
相關文章
相關標籤/搜索