流是Java API的新成員,它容許你以聲明的方式處理數據集合,簡單來講,能夠把它看成數據集的高級迭代器。此外,流還能夠透明地並行處理,你無需寫任何多線程代碼了。java
舉個例子來講明流的好處,有一個簡單的場景,要求返回低熱量的菜餚名稱,並按照卡路里排序,實體代碼以下:
數據庫
package cn.net.bysoft.chapter4; public class Dish { private final String name; private final boolean vegetarian; private final int calories; private final Type type; public Dish(String name, boolean vegetarian, int calories, Type type) { this.name = name; this.vegetarian = vegetarian; this.calories = calories; this.type = type; } public String getName() { return name; } public boolean isVegetarian() { return vegetarian; } public int getCalories() { return calories; } public Type getType() { return type; } @Override public String toString() { return name; } public enum Type { MEAT, FISH, OTHER } }
package cn.net.bysoft.chapter4; import java.util.Arrays; import java.util.List; public class Restaurant { private List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), new Dish("beef", false, 700, Dish.Type.MEAT), new Dish("chicken", false, 400, Dish.Type.MEAT), new Dish("french fries", true, 530, Dish.Type.OTHER), new Dish("rice", true, 350, Dish.Type.OTHER), new Dish("season fruit", true, 120, Dish.Type.OTHER), new Dish("pizza", true, 550, Dish.Type.OTHER), new Dish("prawns", false, 300, Dish.Type.FISH), new Dish("salmon", false, 450, Dish.Type.FISH)); public List<Dish> getMenu() { return menu; } public void setMenu(List<Dish> menu) { this.menu = menu; } }
先用Java7寫一次:編程
// 餐廳對象 Restaurant restaurant = new Restaurant(); // 返回低熱量的菜餚名稱,並按照卡路里排序。 // Java7寫法 List<Dish> lowCaloricDishes = new ArrayList<>(); for (Dish d : restaurant.getMenu()) { if (d.getCalories() < 400) lowCaloricDishes.add(d); } Collections.sort(lowCaloricDishes, new Comparator<Dish>() { @Override 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,它惟一的做用就是做爲一次性的中間容器,在Java8中,實現的細節被放在它本該歸屬的庫中:數組
// 餐廳對象 Restaurant restaurant = new Restaurant(); // Java8寫法 List<String> threeHighCaloricDishNames = restaurant.getMenu().stream() .filter(d -> d.getCalories() > 300) .sorted(comparing(Dish::getCalories)) .map(Dish::getName) .limit(3) .collect(toList());
利用多核架構並行執行這段代碼只須要吧stream()換成parallelStream()就能夠了。多線程
Java8中的Stream API可讓你寫出的這樣的代碼:架構
流的簡短定義是:從支持數據處理操做的源生成的元素序列。編程語言
從上面的代碼中可以體現上面的概念:ide
流只能遍歷一次,遍歷完以後,就說這個流已經消費掉了。能夠從原始數據源在得到一個新的流來從新遍歷一邊,就像迭代器同樣。下面的操做會拋出一個異常:函數式編程
package cn.net.bysoft.chapter4; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Example2 { public static void main(String[] args) { List<String> title = Arrays.asList("Java8", "In", "Action"); Stream<String> s = title.stream(); s.forEach(System.out::println); s.forEach(System.out::println); } }
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.sourceStageSpliterator(Unknown Source) at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source) at cn.net.bysoft.chapter4.Example2.main(Example2.java:12)
使用Collection接口須要用戶去迭代,這稱爲外部迭代。相反,Streams庫使用內部迭代——它幫你把迭代作了,還把獲得的流值存在了某個地方,只要給出一個函數說要幹什麼就能夠了。例如:函數
// 餐廳對象 Restaurant restaurant = new Restaurant(); // 使用for-each循環作外部迭代 List<String> names1 = new ArrayList<>(); for(Dish d : restaurant.getMenu()) names1.add(d.getName()); // 使用迭代器作外部迭代 List<String> names2 = new ArrayList<>(); Iterator<Dish> iterator = restaurant.getMenu().iterator(); while(iterator.hasNext()) { Dish d = iterator.next(); names2.add(d.getName()); } // 使用流作內部迭代 List<String> names3 = restaurant.getMenu().stream() .map(Dish::getName) .collect(toList());
java.util.stream.Stream中的Stream接口定義了許多操做,能夠分爲兩大類:中間操做和終端操做。
操做 | 類型 | 返回 | 參數 | 描述 |
filter | 中間 | Stream<T> | Predicate<T> | T -> boolean |
map | 中間 | Stream<R> | Function<T,R> | T -> R |
limit | 中間 | Stream<T> | ||
sorted | 中間 | Stream<T> | Comparator<T> | (T,T) -> int |
distint | 中間 | Stream<T> | ||
forEach | 終端 | |||
count | 終端 | |||
collect | 終端 |
諸如filter或sorted等中間操做會返回另外一個流。這讓多個操做能夠鏈接起來造成一個查詢。
終端操做會從流的流水線生成結果。
總而言之,流的使用通常包括三件事: