流是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裏選擇元素
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是兩個獨立的操做,但它們合併到同一次遍歷中了(咱們把這種技術叫做循環合併)。
流的使用通常包括三件事: