什麼是流?java
流是Java API的新成員,它容許你以聲明性方式處理數據集合(經過查詢語言來表達,而不是臨時編寫一個實現)。就如今來講你能夠先把它當作是一個遍歷數據集的高級迭代器。此外,流還支持並行,你無需寫任何多線程的代碼!數據庫
定義一個實物類編程
public class Dish { //菜名 private String Name; //是否素食 private boolean vegetarian; //熱量 private Integer Calories; //分類 private Type type; } public enum Type { MEAT,//肉 FISH,//魚 OTHER//其餘 }
定義一個menu集合,如下栗子基於此menu數組
List<Dish> menu = Arrays.asList( new Dish("豬肉燉粉條",false,800,Type.MEAT), new Dish("小炒牛肉",false,700,Type.MEAT), new Dish("宮保雞丁",false,400,Type.MEAT), new Dish("地三鮮",true,530,Type.OTHER), new Dish("水煮菠菜",true,350,Type.OTHER), new Dish("拔絲地瓜",true,120,Type.OTHER), new Dish("火山下雪",true,550,Type.OTHER), new Dish("水煮魚",false,330,Type.FISH), new Dish("因而乎",false,450,Type.FISH) );
舉個栗子:返回熱量低的菜餚名稱數據結構
//java7 List<Dish> lowCaloricDishes = new ArrayList<>(); for(Dish d : menu){ if(d.getCalories() < 400){ lowCaloricDishes.add(d); } } Collections.sort(lowCaloricDishes, new Comparator<Dish>() { @Override public int compare(Dish o1, Dish o2) { return Integer.compare(o1.getCalories(),o2.getCalories()); } }); List<String> lowCaloricDishesName = new ArrayList<>(); for(Dish d : lowCaloricDishes){ lowCaloricDishesName.add(d.getName()); }
//java 8 List<String> lowCalorDishesNames = menu.stream() .filter(s->s.getCalories() < 400) .sorted(Comparator.comparing(Dish::getCalories)) .map(Dish::getName) .collect(toList());
經過使用stream()流來完成這個功能,代碼變的更簡潔了,使用stream你能夠把幾個基礎的操做鏈接起來,來表達複雜的數據處理流水線(在filter後面接上sorted、map和collect操做)。多線程
只要將.stream()更換爲parallelStream()就能夠完成並行操做,並行會在後面將。編程語言
流簡介ide
Java 8中的集合支持了一個新的stream方法,返回一個流。後面你會看到還有不少其餘的方法能夠獲得流。一個簡短的定義就是「從支持數據處理操做的源生成的元素序列」。函數式編程
元素序列:就像集合同樣,流也提供了一個接口,能夠訪問特定元素類型的一組有序值。但流的目的在於表達計算,集合講的是數據。函數
源:流會使用一個提供數據的源,如集合、數組或輸入/輸出資源。從有序集合生成流時會保留原有的順序。
數據處理操做:流的數據處理功能支持相似於數據庫的操做,以及函數式編程語言中的經常使用操做(如filter、map、reduce、find、match、sort等),流能夠順序執行也能夠並行執行。
流還有兩個重要的特色:
流水線:不少流操做自己會返回一個流,這樣多個操做就能夠鏈接起來,造成一個大的流水線。
內部迭代:與使用迭代器顯示迭代集合不一樣,流的迭代操做是在背後進行的。
代碼解析:
menu.stream() :從menu源得到流
.filter(s->s.getCalories() < 400) : 過濾複合lambda表達式的數據
.map(Dish::getName) : 獲取菜名
.limit(3) : 只取前三個
.collect(toList()); : 將結果保存在另外一個List中
結果:[拔絲地瓜, 水煮魚, 水煮菠菜]
流與集合
集合是一個內存中的數據結構,它包含數據結構中目前全部的值-集合中的每一個元素都得先算出來才能添加到集合中,相似於存在DVD中的電影,從DVD上讀出全部信息,
流是在概念上固定的數據結構(你不能添加或刪除 集合能夠),氣元素是按需計算的,流就像是一個延遲建立的集合:只有在消費者須要的時候纔會計算值,相似於在互聯網上看電影。
只遍歷一次
流和迭代器相似,只能遍歷一次。遍歷完以後,咱們就說這個流已經被消費掉了。因此能夠從原始數據源那裏再得到一個新的流從新遍歷一遍。
外部迭代與內部迭代
使用Collection接口須要用戶去作迭代如foreach,這稱爲外部迭代。使用Streams庫則是內部迭代-它幫你把迭代作了,還把獲得的流值存在了某個地方,你只要給出一個函數便可。
List<String> lowCaloricDishesName = new ArrayList<>(); for (Dish d : lowCaloricDishes) { lowCaloricDishesName.add(d.getName()); } //for-each 內部實際上是Iterator Iterator<Dish> iterator = menu.iterator(); while(iterator.hasNext()){ Dish d = iterator.next(); lowCaloricDishesName.add(d.getName()); }
//沒有迭代 List<String> lowCalorDishesNames = menu.stream() .map(Dish::getName) //提取菜名 .collect(toList());
內部迭代與外部迭代的區別:
外部迭代一個集合時,顯示的獲取每一個項目再加以處理。
(如:你:大兒子咱們如今把地上的玩具都收起來吧,還有玩具嗎?
女兒:「有,球」,
你:「好,把球放框裏吧,還有嗎?」
女兒:「有,還有積木」
你:「好,還有嗎?」
女兒:「沒了,沒有了」
你:「ok,收好了」)
而內部迭代時,項目能夠透明地並行處理,或者用更優化的順序進行處理。
(如:你只須要說:「大兒子把地上全部的玩具都放進盒子裏」就行了。 內部迭代的好處:第1、你大兒子能夠左手拿一個球,右手拿一個積木,第2、你大兒子能夠選擇先拿離盒子比較近的東西,而後再拿別的。)
流操做
java.util.stream.Stream中的stream藉口定義了許多操做。他們能夠分爲兩大類:中間操做和終端操做。
中間操做:
如filter或sorted等中間操做會返回另外一個流,這讓多個操做能夠鏈接起來造成一個查詢,除非流水線上觸發一個終端操做,不然中間操做不會執行任何處理。
終端操做:
如count,foreach終端操做會從流的流水線生成結果,其結果是任何不適流的值,好比List、Integer,甚至void。例如,下面的流水線中forEach是一個返回void的終端操做。
menu.stream().forEach(System.out::println);
獲取全部熱量高於300的菜去掉重複值的數量
long count = menu.stream().filter(s->s.getCalories()>300).distinct().count();
使用流
使用流通常包括三件事:一個數據源(執行查詢)、一箇中間操做連(造成一條流水線)、一個終端操做(執行流水線,並生成結果)。
到目前爲止見過的流的 中間操做有:
filter:用於過濾集合 Lambda使用的函數式接口是Predicate<T> 函數描述符是T -> boolean
map:用於映射到一個新的類型 函數式接口是Function<T,R> 函數描述符是 T -> R
limit: 用於截斷流 獲取前幾個
sorted:用於排序流 函數式接口是Comparator<T> 函數描述符是 (T,T) -> int
distinct: 用戶去重
終端操做有:
forEach: 消費流中的每一個元素並對其應用lambda表達式
count: 返回流中元素的個數,long
collect: 把流規約成一個集合,好比List、Map、Integer等
小結:
1.流是從支持數據處理操做的源生成的一系列元素
2.流利用內部迭代:迭代經過filter、map、sorted等操做被抽象掉了
3.流操做有兩類:中間操做和終端操做
4.filter和map等中間操做會返回一個流,並能夠鏈接一塊兒,能夠用他們來設置一條流水線,但不會生成任何結果
5.forEach和count等終端操做會返回一個非流的值,並處理流水線以返回結果
6.流中的元素是按需計算的