選取單一元素
直覺來說選取單一元素肯定會比選取多個要簡單得多,不過這裡也存在一些問題。我們先看下一般的做法的問題是什麼,然後再看下如何用lambda表達式來解決它。
我們先新建一個方法來找出一個以特定字母開頭的元素,然後再列印出來。
複製代碼代碼如下:
public static void pickName(
final List<String> names, final String startingLetter) {
String foundName = null;
for(String name : names) {
if(name.startsWith(startingLetter)) {
foundName = name;
break;
}
}
這個方法簡直跟剛過去的垃圾車一樣臭不可聞。我們先是新建了一個foundName的變量,然後初始化成null――這個就是惡臭之源。我們必須檢查是否為空,不然的話就會拋出一個NullPointerException或一個錯誤回應。我們也用了一個外部迭代器來循環列表,如果找到了想要的元素之後還得跳出這個循環,這又加重了原來的臭味:基本類型偏執,命令式風格,可變性,全齊活了。一旦退出循環後,我們還得先檢查下結果,然後才能進行列印。這麼點任務居然寫了這麼長的程式碼。
我們來重新分析下這個問題。我們只是希望能選出第一個匹配的元素,並且能安全的處理不存在這樣一個元素的情況。我們來用lambda表達式重寫這個pickName方法。
複製代碼代碼如下:
public static void pickName(
final List<String> names, final String startingLetter) {
final Optional<String> foundName =
names.stream()
.filter(name ->name.startsWith(startingLetter))
.findFirst();
System.out.println(String.format("A name starting with %s: %s",
startingLetter, foundName.orElse("No name found")));
}
JDK裡面一些強大的功能使得這段程式碼更得非常簡潔。首先我們用filter方法取得了所有滿足條件的元素,然後用了Stream類別的findFirst方法選取了傳回集合的第一個元素。這個方法回傳的是一個Optional對象,這就是Java裡面官方認證的null變數的除臭劑。
Optional類別非常有用,你不用管結果是不是存在。它使得我們免受空指針異常的煩惱,並且更明確的指明了沒有結果也是一種可能的結果。透過isPresent()方法我們可以知道結果是不是存在,想取得結果值的話可以使用get()方法。我們也可以使用(這個方法名稱能讓你震驚)orElse方法為它設定一個預設值,就像前面程式碼裡的那樣。
我們用之前一直在用的friends集合來驗證下我們這個pickName方法。
複製代碼代碼如下:
pickName(friends, "N");
pickName(friends, "Z");
這段程式碼選取出第一個符合的元素,如果沒找到,列印出一個友善的提示訊息。
複製代碼代碼如下:
A name starting with N: Nate
A name starting with Z: No name found
findFirst()方法和Optinal類別的結合使用減少了我們的程式碼量,看起來感覺還不錯。不過Optional類的功能遠不止這些。比如說,除了當物件不存在的時候能提供一個預設值外,如果結果存在的話還可以用它來運行一段程式碼,或者一個lambda表達式,像這樣:
複製代碼代碼如下:
foundName.ifPresent(name -> System.out.println("Hello " + name));
跟命令式的選取第一個符合名字的程式碼比起來,流式的優雅的函數式風格看真來更棒一些。不過這個調用流的版本裡是不是做的事情有點太多了(譯註:先選出了所有匹配的再返回第一項)?當然不是,這些方法非常智能,它們可以按需工作(在後面113頁的Stream的惰性求值中我們會深入探討這一點)。
選取單一元素的例子展示了JDK函式庫更多強大的功能,下面我們來看下lambda表達式如何根據一個集合,來求出一個想要的值。