重走Java基礎之Streams(二)

來源:重走Java基礎之Streams(二)
作者:知秋(極樂科技知乎專欄原創作者)
博客:一葉知秋


接上篇重走Java基礎之Streams(一)

Processing Order

Stream對象的處理可以是順序或并行。在** sequential **模式中,按照“Stream”的源的順序處理元素。 如果Stream是有序的(例如SortedMap實現或List ),處理過程保證匹配源的排序。 然而,在其他情況下,應注意不要依賴于順序(參見:是Java的HashMap``keySet()迭代順序一致?**)。

Example:

List<Integer> integerList = Arrays.asList(0, 1, 2, 3, 42); 

// sequential 
long howManyOddNumbers = integerList.stream()
                                      .filter(e -> (e % 2) == 1).count(); 

System.out.println(howManyOddNumbers); // Output: 2

Live on Ideone

Parallel(并行)模式允許在多個核上使用多個線程,但不能保證處理元素的順序。

如果在順序的Stream上調用多個方法,則不必調用每個方法。 例如,如果一個Stream被過濾,并且元素的數量減少到一,則不會發生對諸如sort的方法的后續調用。 這可以提高順序的Stream的性能 - 這是一個并行的Stream不可能實現的優化。

Example:

// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
                                              .filter(e -> (e % 2) == 1).count();

System.out.println(howManyOddNumbersParallel); // Output: 2

Live on Ideone

Differences from Containers (or Collections)

雖然一些操作可以在Containers和Streams上執行,但它們最終用于不同的目的并支持不同的操作。 容器更注重元素的存儲方式以及如何有效地訪問這些元素。 另一方面,Stream不提供對其元素的直接訪問和操縱; 它更專用于作為集體實體的對象組并且作為整體對該實體執行操作。 Stream和Collection是用于這些不同目的的單獨的高級抽象。

Consuming Streams

A Stream將僅在有終端操作時被遍歷,如 count()collect()forEach()。否則,不會對Stream 執行任何操作。

在下面的示例中,沒有將終端操作添加到Stream,因此 filter() 操作不會調用,并且不會產生輸出,因為peek()不是終端操作

IntStream.range(1, 10).filter(a -> a % 2 == 0).peek(System.out::println);

Live on Ideone

這是一個具有有效終端操作的 Stream 序列,因此產生一個輸出。
你也可以使用forEach而不是peek:

IntStream.range(1, 10).filter(a -> a % 2 == 0).forEach(System.out::println); 

Live on Ideone

Output:

2
4
6
8

在執行終端操作之后, Stream 被執行消耗,不能被重復使用。

一般來說,Stream的操作如下圖所示:

NOTE: 即使沒有終端操作,也始終執行參數檢查

try {
      IntStream.range(1, 10).filter(null);
  } catch (NullPointerException e) {
      System.out.println("We got a NullPointerException as null was passed as an argument to filter()");
  }

Live on Ideone

Output:

We got a NullPointerException as null was passed as an argument to filter()

Creating a Frequency Map

groupingBy(classifier,downstream)collector允許將Stream元素集合通過對組中的每個元素進行分類到一個Map,并對分類在同一組中的元素執行后續操作。

這個原則的一個典型例子是使用Map 來計算Stream。在這個例子中,分類器是簡單的identity函數,它返回元素as-is。后續操作計算等于元素的數量,使用counting()

Stream.of("apple", "orange", "banana", "apple")
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .entrySet()
        .forEach(System.out::println);

后續操作本身是一個收集器(Collectors.counting() ),對String類型的元素進行操作,并生成類型為Long的結果。 collect方法調用的結果是一個Map。

This would produce the following output:

banana=1
orange=1
apple=2

Infinite Streams 無限流

可以生成一個不結束的Stream。 在無限的Stream上調用終端方法導致Stream進入無限循環。 一個Stream的limit方法可以用于 限制Java處理的Stream的術語數。

這個例子生成一個所有自然數的Stream,從數字1開始。Stream的每個連續項比上一個高一個。 通過調用這個Stream的limit方法,只有Stream的前5個項被考慮和打印。

// Generate infinite stream - 1, 2, 3, 4, 5, 6, 7, ...
IntStream naturalNumbers = IntStream.iterate(1, x -> x + 1);

// Print out only the first 5 terms
naturalNumbers.limit(5).forEach(System.out::println);

Output:

1
2
3
4
5

Collect Elements of a Stream into a Collection 將流的元素收集到集合中

Collect with toList() and toSet()

通過Stream.collect操作可以輕松地將Stream元素收集到容器中 :

System.out.println(Arrays
    .asList("apple", "banana", "pear", "kiwi", "orange")
    .stream()
    .filter(s -> s.contains("a"))
    .collect(Collectors.toList())
);
// prints: [apple, banana, pear, orange]

其他集合實例,例如Set,可以通過使用其他Collectors內置方法。 例如,Collectors.toSet()收集 Stream into到一個 Set中。

Explicit(顯式) control over the implementation of List or Set

根據Collectors#toList()Collectors#toSet()文檔,不能保證所返回ListSet的類型,可變性, 序列化或線程安全。

為了顯式控制要返回的實現,可以使用Collectors#toCollection(Supplier)**,從而可以返回一個指定類型的新的空集合。

// syntax with method reference
System.out.println(strings
        .stream()
        .filter(s -> s != null && s.length() <= 3)
        .collect(Collectors.toCollection(ArrayList::new))
);

// syntax with lambda
System.out.println(strings
        .stream()
        .filter(s -> s != null && s.length() <= 3)
        .collect(Collectors.toCollection(() -> new LinkedHashSet<>()))
);

Parallel Stream

Note: 在決定使用哪個Stream之前,請先看看ParallelStream vs Sequential Stream的對比

當你想同時并發執行Stream操作時,你可以使用這些方法。

List<String> data = Arrays.asList("One", "Two", "Three", "Four", "Five");
Stream<String> aParallelStream = data.stream().parallel();

Or:

Stream<String> aParallelStream = data.parallelStream();

要執行為并行流定義的操作,請調用終端運算符:

aParallelStream.forEach(System.out::println);

(A possible) output from the parallel Stream:

Three
Four
One
Two
Five

順序可能會改變,因為所有的元素被并行處理(這個可能使它更快)。當順序無關緊要時使用parallelStream

性能影響

在涉及網絡的情況下,并行的 Stream可以降低應用的整體性能,因為所有并行的 Stream對于網絡使用公共的fork-join線程池。

另一方面,在許多其他情況下,根據當前運行的CPU中可用內核的數量,并行的 Stream可以顯著提高性能。

本文完!


在學習過程如果有任何疑問,請來極樂網提問,或者掃描下方二維碼,關注極樂官方微信,在平臺下方留言。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,948評論 18 139
  • 來源:重走Java基礎之Streams(四)作者:知秋博客:一葉知秋轉載請注明來源和作者! 接上篇重走Java基礎...
    極樂君閱讀 482評論 0 3
  • 本文采用實例驅動的方式,對JAVA8的stream API進行一個深入的介紹。雖然JAVA8中的stream AP...
    浮梁翁閱讀 25,873評論 3 50
  • Int Double Long 設置特定的stream類型, 提高性能,增加特定的函數 無存儲。stream不是一...
    patrick002閱讀 1,282評論 0 0
  • 作者:知秋來源:重走Java基礎之Streams(一) 因為經常逛stackoverflow,最近也在看react...
    極樂君閱讀 549評論 0 2