java8 streams學習筆記

java8 streams 學習筆記

什麼是流

Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操作;高級版本的 Stream,用戶只要給出需要對其包含的元素執行什麼操作,比如 “過濾掉長度大於 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。

Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次後即用盡了,就好比流水從面前流過,一去不復返。

而和迭代器又不同的是,Stream 可以並行化操作,迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完後再讀下一個 item。而使用並行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然後將結果一起輸出。Stream 的並行操作依賴於 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。

生成流

常見方式(從Collection)

  • Collection.stream()
List<String> list = Arrays.asList(strArray);
stream = list.stream();
  • Collection.parallelStream()(多線程
List<String> list = Arrays.asList(strArray);
stream = list.parallelStream();

從 BufferedReader

java.io.BufferedReader.lines()

Arrays.stream(T array) or Stream.of()

String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);

靜態工廠

java.util.stream.IntStream.range()

IntStream.range(1, 3).forEach(System.out::println);

java.nio.file.Files.walk()

自己構建

java.util.Spliterator

其它

Random.ints()

BitSet.stream()

Pattern.splitAsStream(java.lang.CharSequence)

JarFile.stream()

流的操作類型

Intermediate

一個流可以後面跟隨零個或多個 intermediate 操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。

方法

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

Terminal

一個流只能有一個 terminal 操作,當這個操作執行後,流就被使用“光”了,無法再被操作。所以這必定是流的最後一個操作。Terminal 操作的執行,纔會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。

方法

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

short-circuiting

  • 對於一個 intermediate 操作,如果它接受的是一個無限大(infinite/unbounded)的 Stream,但返回一個有限的新 Stream。
  • 對於一個 terminal 操作,如果它接受的是一個無限大的 Stream,但能在有限的時間計算出結果。

當操作一個無限大的 Stream,而又希望在有限時間內完成操作,則在管道內擁有一個 short-circuiting 操作是必要非充分條件。

方法

anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

常用的流

map

List<Integer> list1 = list.stream().map(t->t.getAge()).collect(Collectors.toList());

filter

List<Integer> list1 = list.stream().filter(t->t.id>2).collect(Collectors.toList());

list轉map

shopIds.stream().collect(Collectors.toMap(t->t,t->false))

forEach

roster.stream().forEach(p -> System.out.println(p.getName()));

reduce

這個方法的主要作用是把 Stream 元素組合起來。它提供一個起始值(種子),然後依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。從這個意義上說,字符串拼接、數值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相當於

Integer sum = integers.reduce(0, (a, b) -> a+b);

Integer sum = integers.reduce(0, Integer::sum);

也有沒有起始值的情況,這時會把 Stream 的前面兩個元素組合起來,返回的是 Optional。

reduce 的用例

// 字符串連接,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);

用 Collectors 來進行 reduction 操作

這個比較重要,每個操作流一般都要使用Collectors進行歸組

比較常用的有:

  • toList()
  • toSet()
  • groupingBy/partitioningBy
  • toMap
  • joining

自己生成流

Stream.generate

通過實現 Supplier 接口,你可以自己來控制流的生成。這種情形通常用於隨機數、常量的 Stream,或者需要前後元素間維持着某種狀態信息的 Stream。把 Supplier 實例傳遞給 Stream.generate() 生成的 Stream,默認是串行(相對 parallel 而言)但無序的(相對 ordered 而言)。由於它是無限的,在管道中,必須利用 limit 之類的操作限制 Stream 大小。

Stream.iterate

iterate 跟 reduce 操作很像,接受一個種子值,和一個 UnaryOperator(例如 f)。然後種子值成爲 Stream 的第一個元素,f(seed) 爲第二個,f(f(seed)) 第三個,以此類推。

參考資料

https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章