一. 流的概念java
流是Java API的新成員,它容許你以聲明性的方式處理數據集合(經過查詢語句來表達,而不是臨時編寫一個實現)。數據結構
Stream API的特性:app
-- 聲明性:更簡潔,更易讀;ide
-- 可複合:更靈活;性能
-- 可並行:性能更好。優化
1.流的定義:從支持數據處理操做的源生成的元素序列。this
流有兩個重要特色:spa
-- 流水線:不少流操做自己會返回一個流,這樣多個操做就能夠連接起來,造成一個大的流水線;接口
-- 內部迭代:與使用迭代器顯式迭代的集合不一樣,流的迭代操做是在背後進行的。three
案例:
public class Dish { private final String name; private final int calories; private final boolean vegetarian; private final DishType type; public Dish(String name, boolean vegetarian, int calories, DishType type) { this.name = name; this.vegetarian = vegetarian; this.calories = calories; this.type = type; } public String getName() { return name; } public int getCalories() { return calories; } public boolean isVegetarian() { return vegetarian; } }
public static void main(String[] args) { List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, DishType.MEAT), new Dish("prawns", false, 300, DishType.FISH), new Dish("chicken", false, 700, DishType.MEAT), new Dish("rice", true, 350, DishType.OTHER) ); List<String> threeHighCaloricDishNames = menu.stream() //菜單流Stream<Dish> .filter(d -> d.getCalories() > 300) //Stream<Dish> .map(Dish::getName) //Stream<String> .limit(3) //Stream<String> .collect(toList()); //List<String>,collect操做開始處理流水線 System.out.println(threeHighCaloricDishNames); //結果:[pork, chicken, rice] }
數據源:menu,它給流提供一個元素序列,而後對流應用一系列數據處理操做:filter、map、limit和collect。除了collect以外,全部這些操做都會返回一個流,這樣它們就能夠接成一條流水線,因而就能夠當作對源的一個查詢。最後collect開始處理流水線,並返回結果。在調用collect以前,沒有任何結果產生,實際上根本沒有從menu裏選擇元素。能夠理解爲:鏈中的方法都在排隊等待,直到調用collect。
2.流操做
流操做分爲中間操做和終端操做。能夠鏈接起來的流操做稱爲中間操做,關閉流的操做稱爲終端操做。
中間操做:中間操做會返回另外一個流。這讓多個操做能夠鏈接起來造成一個查詢。
除非流水線上觸發一個終端操做,不然中間操做不會執行任何處理。這是由於中間操做通常均可以合併起來,在終端操做時一次性所有處理。
終端操做:終端操做會從流的流水線生成結果。其結果是任何不是流的值。
public static void main(String[] args) { List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, DishType.MEAT), new Dish("prawns", false, 300, DishType.FISH), new Dish("chicken", false, 700, DishType.MEAT), new Dish("rice", true, 350, DishType.OTHER) ); List<String> names = menu.stream() .filter(d -> { System.out.println("filtering " + d.getName()); return d.getCalories() > 130; }) .map(d -> { System.out.println("mapping " + d.getName()); return d.getName(); }) .limit(3) .collect(toList()); System.out.println(names); } 輸出結果: filtering pork mapping pork filtering prawns mapping prawns filtering chicken mapping chicken [pork, prawns, chicken]
根據上例能夠發現,儘管filter和map是兩個獨立的操做,但它們合併到同一次遍歷中了(這種技術稱爲循環合併)。
二. 流與集合的異同
集合與流之間的差別在於何時進行計算。
集合:是一個內存中的數據結構,它包含數據結構中目前全部的值--集合中的每一個元素都得先算出來才能添加到集合中。
流:是在概念上固定的數據結構(不能添加或刪除元素),其元素師按需計算的。
流就像是一個延遲建立的集合:只有在消費者要求的時候纔會計算值。
1.流只能遍歷一次
流和迭代器相似,只能遍歷一次,遍歷後說明這個流已經被消費了。
public static void main(String[] args) { List<String> title = Arrays.asList("dispatch", "settlement", "wyvern"); Stream<String> s = title.stream(); s.forEach(System.out::println); s.forEach(System.out::println); //當執行到這裏時會報IllegalStateException: stream has already been operated upon or closed }
2.集合和流在遍歷數據方式的區別
Collection接口須要作迭代(for-each),稱爲外部迭代。
Stream庫使用內部迭代。
內部迭代的優勢:項目能夠透明地並行處理,或者用更優化的順序進行處理。
內部迭代的前提:你已經預先定義好了可以隱藏迭代的操做列表,例如filter或map。