一、前言
之前淺談Stream的collect()操作,就與Collector相關,這也是與Stream同時新創的介面,官方文件一開頭又提到
mutable reduction operation
A mutable reduction operation that accumulates input elements into a mutable result container, optionally transforming the accumulated result into a final representation after all input elements have been processed. Reduction operations can be performed either sequentially or in parallel.
簡單的說,擁有組合函數(combining function)、資料結構(data structure)或某些情況下有預設值、初始值,進行一系列組合拳,可以依照順序出拳,也可以同時出拳的華麗操作。
目錄
- 一、前言
-
二、Collectors
- (2.1) Collectors.toCollection()
- (2.2) Collectors.toList()
- (2.3) Collectors.toUnmodifiableList()
- (2.4) Collectors.toSet()
- (2.5) Collectors.toUnmodifiableSet()
- (2.6) Collectors.toMap()
- (2.7) Collectors.toUnmodifiableMap()
- (2.8) Collectors.joining()
- (2.9) Collectors.counting()
- (2.10) Collectors.collectingAndThen()
- (2.11) Collectors.summarizingDouble/Long/Int()
- (2.12) Collectors.averagingDouble/Long/Int()
- (2.13) Collectors.summingDouble/Long/Int()
- (2.14) Collectors.maxBy/minBy()
- (2.15) Collectors.groupingBy()
- (2.16) Collectors.partitioningBy()
- (2.17) Collectors.teeing()
- (2.18) Collectors.filtering()
- (2.19) Collectors.mapping()
- (2.20) Collectors.flatMapping()
- 三、自定義Collector
- 四、總結
二、Collectors
Collectors 是 JDK 中的實用程式類之一,它包含許多實用程式函數。它主要作為最後一步與 Stream API 一起使用。該類別是stream套件中的一部分,可以這樣導入
xxxxxxxxxx
import static java.util.stream.Collectors.*;
除此之外,我們可以選擇單一類別導入
xxxxxxxxxx
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
(2.1) Collectors.toCollection()
如果我們想使用自定義實現,則需要使用toCollection()收集器以及我們選擇的提供的集合。
範例:創建一個 Stream 實例,然後將它們收集到 LinkedList 實例中
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toCollection();
}
public static void toCollection() {
List<Integer> result = Stream.iterate(1, x -> x + 2).limit(4)
.collect(Collectors.toCollection(LinkedList::new));
result.forEach(System.out::println);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
1
3
5
7
(2.2) Collectors.toList()
範例:創建一個 Stream 實例,然後將它們收集到 List 中
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toList();
}
public static void toList() {
List<Integer> result = Stream.iterate(0, x -> x + 2).limit(4)
.collect(Collectors.toList());
result.forEach(System.out::println);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
0
2
4
6
(2.3) Collectors.toUnmodifiableList()
Java 10 引入了一種將 Stream 元素累積到不可修改的 List 中的便捷方法:
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toUnmodifiableList();
}
public static void toUnmodifiableList(){
List<Integer> result = Stream.iterate(0, x -> x + 1).limit(4)
.collect(Collectors.toUnmodifiableList());
result.forEach(System.out::println);
// throw by UnsupportedOperationException
result.add(11);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
0
1
2
3
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
at Collectors17.toUnmodifiableList(Collectors17.java:27)
at Collectors17.main(Collectors17.java:8)
(2.4) Collectors.toSet()
toSet() 收集器可用於收集 Set 實例中的所有 Stream 元素。
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toSet();
}
public static void toSet() {
Set<Integer> result = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5).stream()
.collect(Collectors.toSet());
result.forEach(System.out::println);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
1
2
3
4
5
(2.5) Collectors.toUnmodifiableSet()
從 Java 10 開始,我們可以使用 toUnmodifiableSet() 收集器輕鬆創建不可修改的 Set:
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toUnmodifiableSet();
}
public static void toUnmodifiableSet() {
Set<Integer> result = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5).stream()
.collect(Collectors.toUnmodifiableSet());
result.forEach(System.out::println);
// throw by UnsupportedOperationException
result.add(11);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
2
3
4
5
1
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
at Collectors17.toUnmodifiableSet(Collectors17.java:44)
at Collectors17.main(Collectors17.java:10)
(2.6) Collectors.toMap()
toMap() 收集器可用於將 Stream 元素收集到 Map 實例中。為此,我們需要提供兩個函數:keyMapper() 和 valueMapper()。
首先,我們將使用keyMapper() 從 Stream 元素中提取 Map 鍵。 然後,我們可以使用 valueMapper() 來提取與給定鍵關聯的值。
範例:將這些元素收集到一個 Map 中,該 Map 將字串存儲為鍵,將其長度存儲為值:
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toMap1();
}
public static void toMap1() {
Map<String, Integer> result = Arrays.asList("a", "bb", "c", "d").stream()
.collect(Collectors.toMap(Function.identity(), String::length));
result.forEach((x, y) -> System.out.println("key=" + x + ", value=" + y));
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
key=bb, value=2
key=a, value=1
key=c, value=1
key=d, value=1
那麼,如果我們的集合包含重複的元素會發生什麼呢?與 toSet() 相反,toMap() 方法不會靜默過濾重複項,這是可以理解的,因為它如何確定要為此鍵選擇哪個值?
請注意,toMap() 甚至不評估值是否也相等。如果它看到重複的鍵,它會立即引發 IllegalStateException。
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toMap2();
}
public static void toMap2() {
Map<String, Integer> result = Arrays.asList("a", "bb", "c", "d", "bb").stream()
.collect(Collectors.toMap(Function.identity(), String::length));
result.forEach((x, y) -> System.out.println("key=" + x + ", value=" + y));
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
Exception in thread "main" java.lang.IllegalStateException: Duplicate key bb (attempted merging values 2 and 2)
at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:135)
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:182)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at Collectors17.toMap(Collectors17.java:51)
at Collectors17.main(Collectors17.java:12)
在這種情況下,如果發生密鑰衝突,我們應該使用帶有另一個簽名的 toMap():
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toMap3();
}
public static void toMap3() {
Map<String, Integer> result = Arrays.asList("a", "bb", "c", "d", "bb").stream()
.collect(Collectors.toMap(Function.identity(), String::length, (item, identicalItem) -> item));
result.forEach((x, y) -> System.out.println("key=" + x + ", value=" + y));
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
key=bb, value=2
key=a, value=1
key=c, value=1
key=d, value=1
(2.7) Collectors.toUnmodifiableMap()
與 List和 Set類似,Java 10 引入了一種將 Stream 元素收集到不可修改的 Map 中的簡單方法:
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
toUnmodifiableMap();
}
public static void toUnmodifiableMap() {
Map<String, Integer> result = Arrays.asList("a", "bb", "c", "d").stream()
.collect(Collectors.toUnmodifiableMap(Function.identity(), String::length));
result.forEach((x, y) -> System.out.println("key=" + x + ", value=" + y));
// throw by UnsupportedOperationException
result.put("ccc", 3);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
key=bb, value=2
key=d, value=1
key=c, value=1
key=a, value=1
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
at java.base/java.util.ImmutableCollections$AbstractImmutableMap.put(ImmutableCollections.java:1072)
at Collectors17.toUnmodifiableMap(Collectors17.java:72)
at Collectors17.main(Collectors17.java:12)
(2.8) Collectors.joining()
joining() 可用於連接 Stream
範例:將元素串接起來
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
joining1();
}
public static void joining1() {
String result = Arrays.asList("a", "bb", "c", "d", "bb").stream()
.collect(Collectors.joining());
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
abbcdbb
我們還可以指定自定義分隔符、前綴和後綴:
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
joining2();
}
public static void joining2() {
String result = Arrays.asList("a", "bb", "c", "d", "bb").stream()
.collect(Collectors.joining(";", "PRE-", "-POST"));
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
PRE-a;bb;c;d;bb-POST
(2.9) Collectors.counting()
counting() 是一個簡單的收集器,允許對所有 Stream 元素進行計數。
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
counting();
}
public static void counting() {
long result = Arrays.asList("a", "bb", "c", "d", "bb").stream()
.collect(Collectors.counting());
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
5
(2.10) Collectors.collectingAndThen()
CollectingAndThen() 是一個特殊的收集器,它允許我們在收集結束后立即對結果執行另一個操作。
範例:計算各個元素出現的比例
collectingAndThen 的第一個參數是 count() 來取得頻率。
然後您可以透過將其轉換為格式化百分比來處理該頻率。
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
collectingAndThen();
}
public static void collectingAndThen() {
List<String> input = List.of("FOO", "FOO", "FOO", "FOO", "FOO", "BAR",
"BAR", "BAZ", "BAZ", "BAZ", "DOO", "DOO");
Map<String, String> map = input.stream().collect(Collectors
.groupingBy(a -> a, Collectors.collectingAndThen(
Collectors.counting(),
count -> "%.2f%%".formatted((double) count
/ input.size() * 100))));
map.entrySet().forEach(System.out::println);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
BAR=16.67%
DOO=16.67%
FOO=41.67%
BAZ=25.00%
(2.11) Collectors.summarizingDouble/Long/Int()
SummarizingDouble/Long/Int 是一個收集器,它返回一個特殊類,其中包含有關提取元素 Stream 中數值數據的統計資訊。
範例:獲取字串長度的資訊
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
summarizingDouble();
}
public static void summarizingDouble() {
DoubleSummaryStatistics result = Arrays.asList("a", "bb", "ccc", "dd").stream()
.collect(Collectors.summarizingDouble(String::length));
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
DoubleSummaryStatistics{count=4, sum=8.000000, min=1.000000, average=2.000000, max=3.000000}
(2.12) Collectors.averagingDouble/Long/Int()
AveragingDouble/Long/Int 是一個收集器,它只返回提取元素的平均值。
範例:獲得平均字串長度
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
averagingDouble();
}
public static void averagingDouble() {
Double result = Arrays.asList("a", "bb", "ccc", "dd").stream()
.collect(Collectors.averagingDouble(String::length));
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
2.0
(2.13) Collectors.summingDouble/Long/Int()
SummingDouble/Long/Int 是一個收集器,它只返回提取的元素的總和。
範例:獲取所有字串長度的總和
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
summingDouble();
}
public static void summingDouble() {
Double result = Arrays.asList("a", "bb", "ccc", "dd").stream()
.collect(Collectors.summingDouble(String::length));
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
8.0
(2.14) Collectors.maxBy/minBy()
MaxBy 和 MinBy 收集器根據提供的 Comparator 實例返回 Stream 的最大/最小元素。
範例:選擇最大的元素
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
maxBy();
}
public static void maxBy() {
Optional<String> result = Arrays.asList("a", "bb", "ccc", "dd").stream()
.collect(Collectors.maxBy(Comparator.naturalOrder()));
System.out.println(result);
System.out.println(result.get());
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
Optional[dd]
dd
(2.15) Collectors.groupingBy()
通常,我們可以使用 GroupingBy() 收集器按給定屬性對對象進行分組,然後將結果存儲在 Map 實例中。
範例:按字串長度對它們進行分組,並將分組結果存儲在 Set 實例中
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
groupingBy();
}
public static void groupingBy() {
Map<Integer, Set<String>> result = Arrays.asList("a", "bb", "ccc", "dd").stream()
.collect(Collectors.groupingBy(String::length, Collectors.toSet()));
System.out.println(result);
result.forEach((key, value) -> System.out.println(key + ":" + value));
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
{1=[a], 2=[bb, dd], 3=[ccc]}
1:[a]
2:[bb, dd]
3:[ccc]
(2.16) Collectors.partitioningBy()
partitioningBy() 是 groupingBy() 的一個特殊情況,它接受一個 Predicate 實例,然後將 Stream 元素收集到 Map 實例中,該實例將布林值存儲為鍵,將集合存儲為值。在 「true」 鍵下,我們可以找到與給定 Predicate 匹配的元素集合,在 「false」 鍵下,我們可以找到與給定 Predicate 不匹配的元素集合。
範例:按字串長度 是否大於2 對它們進行分組,並將分組結果存儲在 Set 實例中
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
partitioningBy();
}
public static void partitioningBy() {
Map<Boolean, List<String>> result = Arrays.asList("a", "bb", "ccc", "dd").stream()
.collect(Collectors.partitioningBy(s->s.length()>2));
System.out.println(result);
result.forEach((key, value) -> System.out.println(key + ":" + value));
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
{false=[a, bb, dd], true=[ccc]}
false:[a, bb, dd]
true:[ccc]
(2.17) Collectors.teeing()
Java 12 提供了一個內置的收集器,使用兩個不同的收集器,然後將這兩個收集器的結果結合起來,創建一些有意義的東西。
由於這個新的收集器將給定的流向兩個不同的方向發球,因此稱為 teeing()。
xxxxxxxxxx
static <T,R1,R2,R> Collector<T,?,R>
teeing(Collector<? super T,?,R1> downstream1, Collector<? super T,?,R2> downstream2, BiFunction<? super R1,? super R2,R> merger)
* downstream1 - the first downstream collector
* downstream2 - the second downstream collector
* merger - the function which merges two results into the single one
* returns - a Collector which aggregates the results of two supplied colle
範例:計算最小與最大值相加結果
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
teeing();
}
public static void teeing() {
Integer result = Arrays.asList(2, 48, 56, 32, 8, 11, 26, -9, 22, 19).stream()
.collect(Collectors.teeing(Collectors.minBy(Integer::compareTo), Collectors.maxBy(Integer::compareTo),
(a, b) -> a.get() + b.get()));
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
47
(2.18) Collectors.filtering()
Collectors.filtering() 類似於 Stream.filter()。它用於篩選輸入元素,但用於不同的方案。Stream API 中的 filter() 方法用於流鏈中,而這個新的 filtering() 方法是一個收集器,可以與 groupingBy() 一起使用。
使用 filter() 時,首先過濾值,然後對其進行分組。這樣,被過濾掉的值就消失了,沒有它的痕跡。如果我們需要跟蹤,那麼我們需要先分組,然後應用過濾。
範例:提取集合中值大於3的元素
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
filtering();
}
public static void filtering() {
List<Integer> result = List.of(1, 2, 3, 5, 5).stream()
.filter(n -> n > 3)
.collect(Collectors.toList());
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
[5, 5]
(2.19) Collectors.mapping()
該方法是一個downstream收集器,它將函數應用於流中的每個元素,然後收集轉換后的元素。
xxxxxxxxxx
static <T,U,A,R> Collector<T,?,R>
mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream)
* mapper:將 type T 的元素轉換為 type U 的函數。
* downstream:用於累積轉換元素的收集器。
範例:將元素轉為大寫再使用
-
串接起來
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
mapping();
}
public static void mapping() {
List<String> input = List.of("Apple", "Banana", "Apricot", "Atemoya",
"Avocados", "Blueberry", "Blackcurrant", "Ackee",
"Cranberry", "Cantaloupe", "Cherry");
String result = input.stream().collect(Collectors.mapping(String::toUpperCase, Collectors.joining("-")));
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
APPLE-BANANA-APRICOT-ATEMOYA-AVOCADOS-BLUEBERRY-BLACKCURRANT-ACKEE-CRANBERRY-CANTALOUPE-CHERRY
(2.20) Collectors.flatMapping()
Collectors.flatMapping() 類似於 Collectors.mapping(),但具有更細粒度的目標。兩個收集器都接受一個函數和一個收集器,其中元素被收集,但 flatMapping() 函數接受一個元素流,然後由收集器累積。
Collectors.flatMapping() 允許我們跳過中間集合,直接寫入映射到 Collectors.groupingBy() 定義的組的單個容器。
Collectors.mapping() 將所有分組作者的評論映射到收集器的容器,即 List,而這個中間集合被 flatMapping() 刪除,因為它提供了要映射到收集器容器的評論清單的直接流。
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
flatMapping();
}
public static void flatMapping() {
Blog blog1 = new Blog("1", "Nice", "Very Nice");
Blog blog2 = new Blog("1", "Good", "Very Good");
Blog blog3 = new Blog("2", "Disappointing", "Ok", "Could be better");
List<Blog> blogs = List.of(blog1, blog2, blog3);
Map<String, List<List<String>>> authorComments1 = blogs.stream()
.collect(Collectors.groupingBy(Blog::getAuthorName,
Collectors.mapping(Blog::getComments, Collectors.toList())));
System.out.println(authorComments1);
Map<String, List<String>> authorComments2 = blogs.stream()
.collect(Collectors.groupingBy(Blog::getAuthorName,
Collectors.flatMapping(blog -> blog.getComments().stream(),
Collectors.toList())));
System.out.println(authorComments2);
}
static class Blog {
private String authorName;
private List<String> comments;
public Blog(String authorName, String... strComments) {
this.authorName = authorName;
this.comments = Arrays.asList(strComments);
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public List<String> getComments() {
return comments;
}
public void setComments(List<String> comments) {
this.comments = comments;
}
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
{1=[[Nice, Very Nice], [Good, Very Good]], 2=[[Disappointing, Ok, Could be better]]}
{1=[Nice, Very Nice, Good, Very Good], 2=[Disappointing, Ok, Could be better]}
三、自定義Collector
Collector介面定義了一組方法,用於收集、轉換和匯總數據,這使得我們能夠從流中收集到特定的數據結構,如List、Set、Map等,或執行複雜的聚合操作,如分組、分區、規約總結等。
Collector介面包含以下五個主要方法:
- supplier(): 建立新的結果容器,可以是一個容器,也可以是一個累加器實例,總之是用來儲存結果資料的
- accumlator(): 元素進入收集器中的具體處理操作
- finisher(): 當所有元素都處理完成後,在返回結果之前的對結果的最終處理操作,當然也可以選擇不做任何處理,直接返回
- combiner(): 各個子流的處理結果最終如何合併到一起去,例如並行流處理場景,元素會被切分為好多個分片進行並行處理,最終各個分片的數據需要合併為一個整體結果,即通過此方法來指定子結果的合併邏輯
-
characteristics(): 對此收集器處理行為的補充描述,比如此收集器是否允許並行流中處理,是否finisher方法必須要有等等,此處返回一個Set集合,裡面的候選值是固定的幾個可選項。
- UNORDERED 聲明此收集器的總歸約結果與Stream流元素遍歷順序無關,不受元素處理順序影響
- CONCURRENT 聲明此收集器可以多個執行緒並行處理,允許在並行流中進行處理
- IDENTITY_FINISH 聲明此收集器的finisher方法是恆等操作,可以跳過
範例:將Person物件進行排序,並根據特定條件進行分組。
xxxxxxxxxx
public class Collectors17 {
public static void main(String[] args) {
custCollector();
}
public static class Person {
String name;
Integer age;
Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public static class MyCustomCollector<T> implements Collector<Person, List<Person>, Map<String, List<Person>>> {
public Supplier<List<Person>> supplier() {
return ArrayList::new;
}
public BiConsumer<List<Person>, Person> accumulator() {
return (list, person) -> list.add(person);
}
public BinaryOperator<List<Person>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
public Function<List<Person>, Map<String, List<Person>>> finisher() {
return list -> {
Map<String, List<Person>> result = new HashMap<>();
list.sort((p1, p2) -> {
if (p1.age != null && p2.age != null) {
return p1.age.compareTo(p2.age);
} else if (p1.age != null) {
return -1;
} else if (p2.age != null) {
return 1;
} else {
return p1.name.compareTo(p2.name);
}
});
for (Person person : list) {
if (result.containsKey(person.name)) {
result.get(person.name).add(person);
} else {
List<Person> group = new ArrayList<>();
group.add(person);
result.put(person.name, group);
}
}
return result;
};
}
public Set<Collector.Characteristics> characteristics() {
// return EnumSet.of(Collector.Characteristics.UNORDERED);
return Collections.emptySet();
}
}
public static void custCollector() {
List<Person> people = Arrays.asList(
new Person("A", 28),
new Person("B", null),
new Person("C", 27),
new Person("D", 22),
new Person("A", 25),
new Person("B", 24),
new Person("B", 21));
Map<String, List<Person>> result = people.stream().collect(new MyCustomCollector<>());
System.out.println(result);
}
}
輸出結果:
xxxxxxxxxx
[Running] cd "d:\Ethan\workspace\lab\example\text\java-newfeatures\src\" && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\javac Collectors17.java && D:\jdk\zulu17.54.21-ca-jdk17.0.13-win_x64\bin\java Collectors17
{A=[Person [name=A, age=25], Person [name=A, age=28]], B=[Person [name=B, age=21], Person [name=B, age=24], Person [name=B, age=null]], C=[Person [name=C, age=27]], D=[Person [name=D, age=22]]}
建立了一個自訂的Collector,用於對Person物件進行排序和分組。排序規則是基於年齡和姓名的組合,分組規則是基於姓名。
困難在於實作finisher()方法,該方法需要按照自訂的排序和分組規則處理結果容器。在排序過程中,我們考慮了年齡和姓名的組合,確保排序的正確性。在分組過程中,我們根據姓名進行分組,形成最終的分組結果。
四、總結
Java Stream API 中提供 collect() 方法,透過Collector介面來完成自定義數據收集、轉換和聚合的過程。通過實現Collector介面,我們可以根據自己的需求創建特定的收集器,從而滿足複雜的數據處理需求。
Collectors類別是已經實現Collector介面,提供超多實用的方法讓我們可以針對常用的集合(Set、Map、List)使用流的方式處理,實現完美的遷移,進入流的世界。
沒有留言:
張貼留言