Java 8 Stream 之Collectors 類
技術標籤:java8
Collectors 類的靜態工廠方法
工廠方法 | 返回型別 | 作用 |
---|---|---|
toList | List<T> | 把流中所有專案收集到一個 List |
toSet | Set<T> | 把流中所有專案收集到一個 Set,刪除重複項 |
toCollection | Collection<T> | 把流中所有專案收集到給定的供應源建立的集合menuStream.collect(toCollection(), ArrayList::new) |
counting | Long | 計算流中元素的個數 |
sumInt | Integer | 對流中專案的一個整數屬性求和 |
averagingInt | Double | 計算流中專案 Integer 屬性的平均值 |
summarizingInt | IntSummaryStatistics | 收集關於流中專案 Integer 屬性的統計值,例如最大、最小、 總和與平均值 |
joining | String | 連線對流中每個專案呼叫 toString 方法所生成的字串collect(joining(", ")) |
maxBy | Optional<T> | 一個包裹了流中按照給定比較器選出的最大元素的 Optional, 或如果流為空則為 Optional.empty() |
minBy | Optional<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)) |
groupingBy | Map<K, List<T>> | 根據專案的一個屬性的值對流中的專案作問組,並將屬性值作 為結果 Map 的鍵 |
partitioningBy | Map<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
));