上一篇博客一文帶你深刻了解 Lambda 表達式和方法引用我給你們介紹了 Java8 函數式特性中的 Lambda,這篇文章我將繼續討論 stream 流的用法html
聲明:本文首發於博客園,做者:後青春期的Keats;地址:https://www.cnblogs.com/keatsCoder/ 轉載請註明,謝謝!java
首先給你們看一段代碼,讓你們直觀感覺下 Java7 和 Java8 遍歷處理集合的不一樣mysql
Dish 是一個菜餚對象,calories 屬性表示該菜品的卡路里值,name 則是菜品的名稱。咱們須要過濾出卡路里小於400、而後根據卡路里值升序、接着拿到他們的名稱列表並返回程序員
Java7sql
public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes){ List<Dish> lowCaloricDishes = new ArrayList<>(); for(Dish d: dishes){ if(d.getCalories() < 400){ lowCaloricDishes.add(d); } } List<String> lowCaloricDishesName = new ArrayList<>(); Collections.sort(lowCaloricDishes, new Comparator<Dish>() { public int compare(Dish d1, Dish d2){ return Integer.compare(d1.getCalories(), d2.getCalories()); } }); for(Dish d: lowCaloricDishes){ lowCaloricDishesName.add(d.getName()); } return lowCaloricDishesName; }
Java8app
public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes){ return dishes.stream() .filter(d -> d.getCalories() < 400) .sorted(comparing(Dish::getCalories)) .map(Dish::getName) .collect(toList()); }
若是須要多核並行處理,則只需調用 dishes.parallelStream()
便可ide
在 Java8 以前,程序員須要經過 2次遍歷 + 一次集合排序才能完成的工做,Java8 只須要一個鏈式調用就能夠解決。這就是 stream 的強大之處函數
流是 Java API 的新成員,容許程序員以聲明式的方式處理集合數據,而且支持鏈式調用、支持並行處理。用流處理的集合數據高效且易讀。優化
java.lang.IllegalStateException: stream has already been operated upon or closed
流已經被消費掉List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action"); Stream<String> s = names.stream(); s.forEach(System.out::println); s.forEach(System.out::println);
對流的操做能夠分爲兩類,能夠繼續執行下一個流操做的稱爲中間操做(方法的返回值是 Stream),關閉流的操做稱爲終止操做。code
除非流水線上執行終端操做,不然中間操做不會執行任何處理。流會對中間操做進行合併、短路等優化
終端操做會從流的流水線生成結果,返回一個非 Stream 的任意類型值
filter(Predicate<? super T> predicate)
方法能夠將流中知足某條件的元素篩選出來。該方法接收一個謂詞函數,返回流。好比要選出某個蘋果集合中紅色的蘋果
List<Apple> appleList = new ArrayList<>(); List<Apple> redAppleList = appleList.stream().filter(a -> "red".equals(a.getColor())).collect(Collectors.toList());
distinct()
方法會根據元素的 hashCode() 和 equals() 方法對流中元素進行去重操做
limit(n)
方法會返回流的前 n 個元素,對於有序集合List,流會按照添加順序返回前 n 個元素,而無序集合則不會
skip(n)
方法會跳過流的前 n 個元素,能夠經過 skip(m).limit(n) 返回列表中第 m - (m+n) 區間的元素,相似與 mysql 中的 limit m,n
map(Function<? super T, ? extends R> mapper)
方法。該方法接收一個 Function 函數,對流中的每個元素使用。而後能夠返回任意類型的對象。有了該方法,就能夠結合 Lambda 表達式對集合中的元素使用函數進行各類轉換
flatMap()
能夠將流操做中多個流合併成一個流的多個元素。舉個例子:集合 words 有兩個單詞,如今想得到[H, e, l, o, W, r, d]
在 split 方法執行完畢後,返回的是 Stream(String[]) 對象,而此時若是執行 map 方法,返回的就是多個流的集合(這個例子中就是兩個 Stream(String)),這時是沒法繼續接下來的 distinct 操做的,所以須要 flatMap 將兩個 Stream 扁平化成一個 Stream,而後進行操做
List<String> words = Arrays.asList("Hello", "World"); List<String> charList = words.stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
該方法的方法聲明 flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
中能夠看出,他所使用的函數式接口 Function 第二個泛型 R 必須是 Stream 流。即函數式接口的抽象方法返回值必須是 Stream 流及其子類對象。
anyMatch
方法能夠回答「流中是否存在至少一個複合謂詞條件的元素」返回 boolean 類型的值,所以是一個終端操做,例如
List<Integer> num = Arrays.asList(1, 2, 3, 4, 5, 6); if (num.stream().anyMatch(n -> n % 3 == 0)) { System.out.println("集合中有元素是3的整數倍"); }
控制檯會輸出'集合中有元素是3的整數倍',由於集合中 三、6都是3的整數倍,符合謂詞的條件
allMatch
方法和 anyMatch
方法原理相似,可是它僅當全部元素知足謂詞條件時,返回 true。
noneMatch
與 allMatch
正好相反,僅當全部元素不知足謂詞條件時,返回 true
ps:和 && || 運算符相似,以上三個操做都用到了短路的思想來提升效率。
findAny()
該方法返回當前流中的任意元素,能夠和其餘流操做結合使用,這裏須要注意 findAny() 返回的結果被 Optional 所包裹,Optional 是 Java8 爲優雅的避免 NPE 所採用的新 API,關於 Optional 的用法我會在下一篇博客和你們討論,敬請期待。這裏須要說明的就是 Optional.ifPresent(Consumer<? super T> consumer) 表示當 Optional 包裹的元素不爲空時,執行 consumer
num.stream().filter(n -> n > 2).findAny().ifPresent(System.out::println);
findFirst()
該方法返回當前流中的第一個元素,通常也和其餘流操做(例如 filter() 過濾)結合使用。與 findAny()
不一樣的是,他必定返回有序集合的第一個知足條件的元素。固然有得必有失,做爲代價,findFirst()
在並行處理時限制更多一些。
reduce(T identity, BinaryOperator<T> accumulator);
方法接收兩個參數:identity 初始值,accumulator 對兩個數的操做。例如求集合中數字的和:
num.stream().reduce(0, (a, b) -> a + b) // 計算完成,返回 21
ps:Lambda 表達式 (a, b) -> a + b)
中 a 是上一輪執行完後的累計值,b 是本次循環流中的元素。經過累加就能夠計算出數字的和
reduce 方法不只能夠求和、求積。甚至能夠計算最大值、最小值。
num.stream().reduce(Integer::max); num.stream().reduce(Integer::min);
碼字不易,若是你以爲讀完之後有收穫,不妨點個推薦讓更多的人看到吧!