《java 8 實戰》讀書筆記 -第四章 引入流

第四章 引入流

1、什麼是流

流是Java API的新成員,它容許你以聲明性方式處理數據集合(經過查詢語句來表達,而不是臨時編寫一個實現)。就如今來講,你能夠把它們當作遍歷數據集的高級迭代器。此外,流還能夠透明地並行處理,你無需寫任何多線程代碼。

下面兩段代碼都是用來返回低熱量的菜餚名稱的,並按照卡路里排序,一個是用Java 7寫的,另外一個是用Java 8的流寫的。java

以前(Java 7):程序員

List<Dish> lowCaloricDishes = new ArrayList<>(); 
for(Dish d: menu){ 
 if(d.getCalories() < 400){ 
 lowCaloricDishes.add(d); 
 } 
} 
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
 public int compare(Dish d1, Dish d2){ 
 return Integer.compare(d1.getCalories(), d2.getCalories()); 
 } 
}); 
List<String> lowCaloricDishesName = new ArrayList<>(); 
for(Dish d: lowCaloricDishes){ 
 lowCaloricDishesName.add(d.getName()); 
}

在這段代碼中,你用了一個「垃圾變量」lowCaloricDishes。它惟一的做用就是做爲一次
性的中間容器。在Java 8中,實現的細節被放在它本該歸屬的庫裏了。數據庫

以後(Java 8):編程

import static java.util.Comparator.comparing; 
import static java.util.stream.Collectors.toList; 
List<String> lowCaloricDishesName = 
 menu.stream() 
 .filter(d -> d.getCalories() < 400) 
 .sorted(comparing(Dish::getCalories))
 .map(Dish::getName) 
 .collect(toList());

爲了利用多核架構並行執行這段代碼,你只須要把stream()換成parallelStream()數組

List<String> lowCaloricDishesName = 
 menu.parallelStream() 
 .filter(d -> d.getCalories() < 400) 
 .sorted(comparing(Dishes::getCalories)) 
 .map(Dish::getName) 
 .collect(toList());

按照Map裏面的類別對菜餚進行分組。數據結構

Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType));
其餘庫:Guava、Apache和lambdaj
爲了給Java程序員提供更好的庫操做集合,前人已經作過了不少嘗試。好比,Guava就是谷歌建立的一個很流行的庫。它提供了multimaps和multisets等額外的容器類。Apache Commons Collections庫也提供了相似的功能。Mario Fusco編寫的lambdaj受到函數式編程的啓發,也提供了不少聲明性操做集合的工具。

流究竟是什麼呢?簡短的定義就是「從支持數據處理操做的源生成的元素序列」。多線程

  • 元素序列——就像集合同樣,流也提供了一個接口,能夠訪問特定元素類型的一組有序值。由於集合是數據結構,因此它的主要目的是以特定的時間/空間複雜度存儲和訪問元素(如ArrayList 與 LinkedList)。但流的目的在於表達計算,好比你前面見到的filter、sorted和map。集合講的是數據,流講的是計算。咱們會在後面幾節中詳細解釋這個思想。
  • ——流會使用一個提供數據的源,如集合、數組或輸入/輸出資源。 請注意,從有序集合生成流時會保留原有的順序。由列表生成的流,其元素順序與列表一致。
  • 數據處理操做——流的數據處理功能支持相似於數據庫的操做,以及函數式編程語言中的經常使用操做,如filter、map、reduce、find、match、sort等。流操做能夠順序執行,也可並行執行。

eg: 選出3條卡路里高於300的菜餚的菜餚的名字架構

import static java.util.stream.Collectors.toList; 
List<String> threeHighCaloricDishNames = 
 menu.stream() 
 .filter(d -> d.getCalories() > 300)
 .map(Dish::getName)
 .limit(3)         //篩選(filter)、提取(map)或截斷(limit)
 .collect(toList()); 
System.out.println(threeHighCaloricDishNames); 
//在調用collect以前,沒有任何結果產生,實際上根本就沒有從menu裏選擇元素

2、流與集合

圖片描述

  • 和迭代器相似,流只能遍歷一次。遍歷完以後,咱們就說這個流已經被消費掉了。
  • 使用Collection接口須要用戶去作迭代(好比用for-each),這稱爲外部迭代。 相反,Streams庫使用內部迭代——它幫你把迭代作了,還把獲得的流值存在了某個地方,你只要給出一個函數說要幹什麼就能夠了。

圖片描述

3、流操做

java.util.stream.Stream中的Stream接口定義了許多操做。它們能夠分爲兩大類:app

  • 中間操做
    能夠鏈接起來的流操做稱爲中間操做,諸如filter或sorted等

圖片描述

  • 終端操做
    關閉流的操做稱爲終端操做,其結果是任何不是流的值,好比List、Integer,甚至void

圖片描述

eg:(注意循環合併)編程語言

List<String> names = 
     menu.stream() 
     .filter(d -> { 
     System.out.println("filtering" + d.getName()); 
     return d.getCalories() > 300; 
     }) 
     .map(d -> { 
     System.out.println("mapping" + d.getName()); 
     return d.getName(); 
     }) 
     .limit(3) 
     .collect(toList()); 
    System.out.println(names);

此代碼執行時將打印:

filtering pork 
    mapping pork 
    filtering beef 
    mapping beef 
    filtering chicken 
    mapping chicken 
    [pork, beef, chicken]

儘管filter和map是兩個獨立的操做,但它們合併到同一次遍歷中了(咱們把這種技術叫做循環合併)。

4、使用流

流的使用通常包括三件事:

  • 一個數據源(如集合)來執行一個查詢;
  • 一箇中間操做鏈,造成一條流的流水線;
  • 一個終端操做,執行流水線,並能生成結果。