1. 程式人生 > 其它 >Java 8 Stream 之Collectors 類

Java 8 Stream 之Collectors 類

技術標籤:java8

Collectors 類的靜態工廠方法

工廠方法返回型別作用
toListList<T>把流中所有專案收集到一個 List
toSetSet<T>把流中所有專案收集到一個 Set,刪除重複項
toCollectionCollection<T>把流中所有專案收集到給定的供應源建立的集合menuStream.collect(toCollection(), ArrayList::new)
countingLong計算流中元素的個數
sumIntInteger對流中專案的一個整數屬性求和
averagingIntDouble計算流中專案 Integer 屬性的平均值
summarizingIntIntSummaryStatistics收集關於流中專案 Integer 屬性的統計值,例如最大、最小、 總和與平均值
joiningString連線對流中每個專案呼叫 toString 方法所生成的字串collect(joining(", "))
maxByOptional<T>一個包裹了流中按照給定比較器選出的最大元素的 Optional, 或如果流為空則為 Optional.empty()
minByOptional<T>一個包裹了流中按照給定比較器選出的最小元素的 Optional, 或如果流為空則為 Optional.empty()
reducing歸約操作產生的型別從一個作為累加器的初始值開始,利用 BinaryOperator 與流 中的元素逐個結合,從而將流歸約為單個值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen轉換函式返回的型別包裹另一個收集器,對其結果應用轉換函式int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
groupingByMap<K, List<T>>
根據專案的一個屬性的值對流中的專案作問組,並將屬性值作 為結果 Map 的鍵
partitioningByMap<Boolean,List<T>>根據對流中每個專案應用謂詞的結果來對專案進行分割槽

Collectors

在Collectors類中有所有的預定義實現。一般常見做法是使用下面的靜態匯入來提高可讀性:

import static java.util.stream.Collectors.*;

或者單個匯入你選擇的collectors:

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

下面所有示例將使用以下演示資料集合

List<String> givenList = Arrays.asList("a", "bb", "ccc", "dd");

Collectors.toList()

ToList 方法可以收集所有流中元素至List例項。需要注意的是,使用該方法不能假設任何List的特定實現,如果你想指定List實現,需要使用toCollection 代替.下面示例建立一個流例項表示元素序列,然後收集至List例項物件中。

List<String> result = givenList.stream().collect(toList());

Collectors.toSet()

ToSet 方法可以收集所有流中元素至Set 例項.需要注意的是,使用該方法不能假設任何Set的特定實現,如果你想指定Set實現,需要使用toCollection 代替.下面示例建立一個流例項表示元素序列,然後收集至Set例物件中

Set<String> result = givenList.stream().collect(toSet());

Set不包含重複元素。如果集合中有重複元素,則set中僅保留一個:

List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
Set<String> result = listWithDuplicates.stream().collect(toSet());
assertThat(result).hasSize(4);

Collectors.toCollection()

你可能已經注意到,使用toSet 和 toList收集器,不能指定其實現。如果想使用特定實現,需要使用toCollection收集器並提供特定集合實現。下面示例建立一個流例項表示元素序列,然後收集至LinkedList例物件中

List<String> result = givenList.stream().collect(toCollection(LinkedList::new))

注意這裡不能使用不可變集合實現。通過自定義Collector實現或使用collectingAndThen。

Collectors.toMap()

ToMap 收集器用於收集流元素至 Map 例項,實現該功能需要提供兩個函式:

  • keyMapper
  • valueMapper

keyMapper 用於從流元素中抽取map 的key,valueMapper抽取與可以關聯的value。
下面示例收集流元素至Map中,儲存字串作為key,其長度作為value:

Map<String, Integer> result = givenList.stream()
  .collect(toMap(Function.identity(), String::length))

Function.identity() 是一個預定義的返回接收引數的快捷函式。
如果我們集合中包含重複元素會怎麼樣?與toSet相反,toMap不能過濾重複元素。這個比較好理解————其如何確定key關聯那個value?

List<String> listWithDuplicates = Arrays.asList("a", "bb", "c", "d", "bb");
assertThatThrownBy(() -> {
    listWithDuplicates.stream().collect(toMap(Function.identity(), String::length));
}).isInstanceOf(IllegalStateException.class);

我們看到,toMap甚至不判斷值是否相等,如果key重複,立刻丟擲IllegalStateException異常。當key衝突時,我們應該使用toMap的另一個過載方法:

Map<String, Integer> result = givenList.stream()
  .collect(toMap(Function.identity(), String::length, (item, identicalItem) -> item));

第三個引數是二元操作,我們可以指定如何處理key衝突。本例中我們可以任意選擇一個,因為兩者的長度始終一樣。

Collectors.collectingAndThen()

CollectingAndThen 是一個特殊收集器,其可以收集完成後再結果上執行另外動作。下面收集流元素至List示例,然後轉換結果為ImmutableList例項:

List<String> result = givenList.stream()
  .collect(collectingAndThen(toList(), ImmutableList::copyOf))

Collectors.joining()

Joining 收集器可以用於連線字串流Stream中的元素。示例如下:

String result = givenList.stream()
  .collect(joining());

結果如下:

"abbcccdd"

還可以指定分隔符、字首和字尾:

String result = givenList.stream()
  .collect(joining(" "));

結果如下:

"a bb ccc dd"

再看一個示例:

String result = givenList.stream()
  .collect(joining(" ", "PRE-", "-POST"));

結果為:

"PRE-a bb ccc dd-POST"

Collectors.counting()

Counting 是一個簡單收集器,返回元素數量:

Long result = givenList.stream()
  .collect(counting());

Collectors.summarizingDouble/Long/Int()

SummarizingDouble/Long/Int 收集器返回流中抽取的數值元素的統計結果型別。下面示例獲取字串長度資訊:

DoubleSummaryStatistics result = givenList.stream()
  .collect(summarizingDouble(String::length));

下面測試程式碼執行通過:

assertThat(result.getAverage()).isEqualTo(2);
assertThat(result.getCount()).isEqualTo(4);
assertThat(result.getMax()).isEqualTo(3);
assertThat(result.getMin()).isEqualTo(1);
assertThat(result.getSum()).isEqualTo(8);

Collectors.averagingDouble/Long/Int()

AveragingDouble/Long/Int 是返回流中抽取元素的平均值,下面示例計算字串長度平均值:

Double result = givenList.stream()
  .collect(averagingDouble(String::length));

Collectors.summingDouble/Long/Int()

SummingDouble/Long/Int 返回抽取元素之和,下面計算所有字串長度之和:

Double result = givenList.stream()
  .collect(summingDouble(String::length));

Collectors.maxBy()/minBy()

MaxBy/MinBy 收集器根據提供的 Comparator 例項,返回流中最大和最小元素,下面計算最大元素:

Optional<String> result = givenList.stream()
  .collect(maxBy(Comparator.naturalOrder()));

需要注意的是,其結果為Optional型別,其強制使用者考慮空集合情況

Collectors.groupingBy()

分組收集器用於根據屬性對元素進行分組並存儲在Map例項中,下面示例根據字串長度進行分組,並把結果儲存在Set中:

Map<Integer, Set<String>> result = givenList.stream()
  .collect(groupingBy(String::length, toSet()));

測試程式碼如下:

assertThat(result)
  .containsEntry(1, newHashSet("a"))
  .containsEntry(2, newHashSet("bb", "dd"))
  .containsEntry(3, newHashSet("ccc"));

第二個引數是一個集合,我們開使用任何集合實現。

Collectors.partitioningBy()

PartitioningBy 是一個特殊分組收集器,依據 Predicate 例項收集流元素至Map中,儲存 Boolean 值作為key,值為集合。 “true” 鍵對應值為已匹配元素的集合,“false” 鍵為非匹配元素集合。示例程式碼如下:

Map<Boolean, List<String>> result = givenList.stream()
  .collect(partitioningBy(s -> s.length() > 2))

結果Map如下:

{false=["a", "bb", "dd"], true=["ccc"]}

Collectors.teeing()

根據目前我們已經學習的內容求最大值和最小值:

List<Integer> numbers = Arrays.asList(42, 4, 2, 24);
Optional<Integer> min = numbers.stream().collect(minBy(Integer::compareTo));
Optional<Integer> max = numbers.stream().collect(maxBy(Integer::compareTo));
// do something useful with min and max

這裡,我們使用兩個不用的收集器,然後合併集合去實現相應業務。在Java 12之前,為了實現這樣功能必須對流執行兩次操作,把中間結果儲存至臨時物件中,最後合併返回。
幸運的是,java 12 提供了內建收集器幫助我們處理這些步驟:我們僅需提供兩個收集器和合並函式。
因為新的收集器tee對流執行兩個不用方向的操作,故稱為T收集器:

numbers.stream().collect(teeing(
  minBy(Integer::compareTo), // The first collector
  maxBy(Integer::compareTo), // The second collector
  (min, max) -> // Receives the result from those collectors and combines them
));