jdk8 stream

獲取一個數據源(source)→ 數據轉換→執行操做獲取想要的結果,每次轉換原有 Stream 對象不改變,返回一個新的 Stream 對象(能夠有屢次轉換),這就容許對其操做能夠像鏈條同樣排列,變成一個管道,html

Stream操做還有兩個基礎的特徵:java

  • Pipelining: 中間操做都會返回流對象自己。 這樣多個操做能夠串聯成一個管道, 如同流式風格(fluent style)。 這樣作能夠對操做進行優化, 好比延遲執行(laziness)和短路( short-circuiting)。
  • 內部迭代: 之前對集合遍歷都是經過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫作外部迭代。 Stream提供了內部迭代的方式, 經過訪問者模式(Visitor)實現。

     

對 Stream 的使用就是實現一個 filter-map-reduce 過程,產生一個最終結果,或者致使一個反作用(side effect)。api

流的操做

接下來,當把一個數據結構包裝成 Stream 後,就要開始對裏面的元素進行各種操做了。常見的操做能夠歸類以下。數組

  • Intermediate:

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered數據結構

  • Terminal:

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator多線程

  • Short-circuiting:

anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limitide

一對多 及 flatmap
Stream<List<Integer>> inputStream = Stream.of(
 Arrays.asList(1),
 Arrays.asList(2, 3),
 Arrays.asList(4, 5, 6)
 );
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());

flatMap 把 input Stream 中的層級結構扁平化,就是將最底層元素抽出來放到一塊兒,最終 output 的新 Stream 裏面已經沒有 List 了,都是直接的數字。性能

例 : 把單詞挑出來
List<String> output = reader.lines().
 flatMap(line -> Stream.of(line.split(REGEXP))).
 filter(word -> word.length() > 0).
 collect(Collectors.toList());

這段代碼首先把每行的單詞用 flatMap 整理到新的 Stream,而後保留長度不爲 0 的,就是整篇文章中的所有單詞了。優化

forEachui

forEach 方法接收一個 Lambda 表達式,而後在 Stream 的每個元素上執行該表達式。

清單 12. 打印姓名(forEach 和 pre-java8 的對比)
// Java 8
roster.stream()
 .filter(p -> p.getGender() == Person.Sex.MALE)
 .forEach(p -> System.out.println(p.getName()));
// Pre-Java 8
for (Person p : roster) {
 if (p.getGender() == Person.Sex.MALE) {
 System.out.println(p.getName());
 }
}

須要注意,forEach 是 terminal 操做,所以它執行後,Stream 的元素就被「消費」掉了,你沒法對一個 Stream 進行兩次 terminal 運算。forEach 不能修改本身包含的本地變量值,也不能用 break/return 之類的關鍵字提早結束循環。

reduce

這個方法的主要做用是把 Stream 元素組合起來。它提供一個起始值(種子),而後依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。

// 字符串鏈接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); 
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); 
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 無起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 過濾,字符串鏈接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").
 filter(x -> x.compareTo("Z") > 0).
 reduce("", String::concat);

  上面代碼例如第一個示例的 reduce(),第一個參數(空白字符)即爲起始值,第二個參數(String::concat)爲 BinaryOperator。這類有起始值的 reduce() 都返回具體的對象。而對於第四個示例沒有起始值的 reduce(),因爲可能沒有足夠的元素,返回的是 Optional,請留意這個區別。

 

min/max/distinct

min 和 max 的功能也能夠經過對 Stream 元素先排序,再 findFirst 來實現,但前者的性能會更好,爲 O(n),而 sorted 的成本是 O(n log n)。

Match

Stream 有三個 match 方法,從語義上說:

  • allMatch:Stream 中所有元素符合傳入的 predicate,返回 true
  • anyMatch:Stream 中只要有一個元素符合傳入的 predicate,返回 true
  • noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true

它們都不是要遍歷所有元素才能返回結果。例如 allMatch 只要一個元素不知足條件,就 skip 剩下的全部元素,返回 false。對清單 13 中的 Person 類稍作修改,加入一個 age 屬性和 getAge 方法。

使用 Match
List<Person> persons = new ArrayList();
persons.add(new Person(1, "name" + 1, 10));
persons.add(new Person(2, "name" + 2, 21));
persons.add(new Person(3, "name" + 3, 34));
persons.add(new Person(4, "name" + 4, 6));
persons.add(new Person(5, "name" + 5, 55));
boolean isAllAdult = persons.stream().
 allMatch(p -> p.getAge() > 18);
System.out.println("All are adult? " + isAllAdult);
boolean isThereAnyChild = persons.stream().
 anyMatch(p -> p.getAge() < 12);
System.out.println("Any child? " + isThereAnyChild);
輸出結果:
All are adult? false
Any child? true

 

Collectors

Collectors 類實現了不少歸約操做,例如將流轉換成集合和聚合元素。Collectors 可用於返回列表或字符串:

  List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 

List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); 
System.out.println("篩選列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合併字符串: " + mergedString);

Stream 的特性能夠概括爲:

  • 不是數據結構
  • 它沒有內部存儲,它只是用操做管道從 source(數據結構、數組、generator function、IO channel)抓取數據。
  • 它也毫不修改本身所封裝的底層數據結構的數據。例如 Stream 的 filter 操做會產生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素。
  • 全部 Stream 的操做必須以 lambda 表達式爲參數
  • 不支持索引訪問
  • 你能夠請求第一個元素,但沒法請求第二個,第三個,或最後一個。不過請參閱下一項。
  • 很容易生成數組或者 List
  • 惰性化
  • 不少 Stream 操做是向後延遲的,一直到它弄清楚了最後須要多少數據纔會開始。
  • Intermediate 操做永遠是惰性化的。
  • 並行能力
  • 當一個 Stream 是並行化的,就不須要再寫多線程代碼,全部對它的操做會自動並行進行的。
  • 能夠是無限的
    • 集合有固定大小,Stream 則沒必要。limit(n) 和 findFirst() 這類的 short-circuiting 操做能夠對無限的 Stream 進行運算並很快完成。

 

本文轉自:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html

相關文章
相關標籤/搜索