Java Stream 的 collect 與 reduce 操作示例詳解
在 Java Stream API 中,collect 和 reduce 是兩種強(qiáng)大的終止操作,用于將流中的元素累積為最終結(jié)果。盡管它們都用于聚合數(shù)據(jù),但實(shí)現(xiàn)機(jī)制和應(yīng)用場景存在顯著差異。本文將從核心概念、使用場景、性能特性等多個(gè)維度進(jìn)行對比分析。
一、核心概念對比
| 特性 | collect(Collector) | reduce(BinaryOperator) |
|---|---|---|
| 操作類型 | 可變?nèi)萜鲄R聚操作 | 不可變值累積操作 |
| 結(jié)果類型 | 可變?nèi)萜鳎ㄈ?List、Set、Map) | 單個(gè)值或容器(需通過 Supplier 創(chuàng)建) |
| 數(shù)據(jù)結(jié)構(gòu) | 支持中途修改的集合 | 不可變對象的累積 |
| 并行處理效率 | 高(可并發(fā)操作獨(dú)立容器后合并) | 低(需按順序累積或復(fù)雜 combiner) |
| 典型應(yīng)用場景 | 數(shù)據(jù)分組、分區(qū)、轉(zhuǎn)換集合類型 | 求和、求最大值、字符串拼接 |
二、collect 方法詳解
collect 是一種可變?nèi)萜鲄R聚操作,通過 Collector 接口實(shí)現(xiàn)復(fù)雜的歸約邏輯。它將流中的元素累積到一個(gè)可變?nèi)萜鳎ㄈ?List、Set、Map)中,并支持進(jìn)一步的處理。
1. 基礎(chǔ)用法示例
// 將 Stream 轉(zhuǎn)換為 List
List<String> names = Stream.of("Alice", "Bob", "Charlie")
.collect(Collectors.toList());
// 將 Stream 轉(zhuǎn)換為 Set 去重
Set<Integer> uniqueNumbers = Stream.of(1, 2, 2, 3, 3, 3)
.collect(Collectors.toSet());
// 使用 toCollection 指定具體集合類型
LinkedList<String> linkedList = Stream.of("a", "b", "c")
.collect(Collectors.toCollection(LinkedList::new));2. 高級用法:分組與分區(qū)
class Person {
private String name;
private int age;
private String city;
// 構(gòu)造器、getter 略
}
// 按城市分組
Map<String, List<Person>> peopleByCity = personStream
.collect(Collectors.groupingBy(Person::getCity));
// 按年齡是否大于18分區(qū)
Map<Boolean, List<Person>> partitionByAge = personStream
.collect(Collectors.partitioningBy(p -> p.getAge() > 18));3. 自定義 Collector
// 自定義 Collector 實(shí)現(xiàn)字符串拼接
Collector<String, StringBuilder, String> stringCollector = Collector.of(
StringBuilder::new, // 創(chuàng)建容器
StringBuilder::append, // 累積元素
(sb1, sb2) -> sb1.append(sb2), // 合并容器
StringBuilder::toString // 最終轉(zhuǎn)換
);
String result = Stream.of("Hello", " ", "World")
.collect(stringCollector); // 輸出:Hello World三、reduce 方法詳解
reduce 是一種不可變值累積操作,通過二元操作(BinaryOperator)將流中的元素依次處理,最終得到一個(gè)值。
1. 基礎(chǔ)用法示例
// 無初始值的 reduce(返回 Optional)
Optional<Integer> sum = Stream.of(1, 2, 3, 4)
.reduce(Integer::sum); // 結(jié)果:10
// 有初始值的 reduce
int product = Stream.of(1, 2, 3, 4)
.reduce(1, (a, b) -> a * b); // 結(jié)果:24
// 復(fù)雜累積:字符串拼接
String concatenated = Stream.of("a", "b", "c")
.reduce("", String::concat); // 結(jié)果:abc2. 并行流中的 reduce
// 并行流中使用帶 combiner 的 reduce
int sum = Stream.of(1, 2, 3, 4)
.parallel()
.reduce(0, // 初始值
Integer::sum, // 累積器
Integer::sum); // 合并器(并行流必須)3. 自定義累積邏輯
// 計(jì)算最大值
Optional<Integer> max = Stream.of(5, 3, 9, 1)
.reduce((a, b) -> a > b ? a : b); // 結(jié)果:9
// 計(jì)算平均值(需要自定義累積器)
class Average {
private int sum;
private int count;
public double get() {
return count > 0 ? (double) sum / count : 0;
}
}
Average average = Stream.of(1, 2, 3, 4, 5)
.reduce(new Average(),
(avg, num) -> {
avg.sum += num;
avg.count++;
return avg;
},
(avg1, avg2) -> {
avg1.sum += avg2.sum;
avg1.count += avg2.count;
return avg1;
});
double result = average.get(); // 結(jié)果:3.0四、核心區(qū)別與選擇策略
| 場景 | collect 更適合 | reduce 更適合 |
|---|---|---|
| 結(jié)果類型 | 集合或復(fù)雜對象 | 單個(gè)值(如數(shù)字、字符串) |
| 并行處理效率 | 高(獨(dú)立容器可并發(fā)合并) | 低(需順序累積或復(fù)雜 combiner) |
| 累積過程是否可變 | 可變(操作集合內(nèi)部元素) | 不可變(生成新對象) |
| 是否需要分組 / 分區(qū) | 是(groupingBy、partitioningBy) | 否 |
| 是否需要多階段處理 | 是(Collector 鏈) | 否 |
五、性能對比與優(yōu)化建議
- 并行流場景
- collect:由于支持并發(fā)操作獨(dú)立容器后合并,性能通常優(yōu)于 reduce
- reduce:若未正確實(shí)現(xiàn) combiner,可能導(dǎo)致并行效率低下
- 大集合處理
- collect:在分組、分區(qū)等復(fù)雜操作中表現(xiàn)更優(yōu)
- reduce:在簡單累積(如求和)中性能相當(dāng),但代碼可能更簡潔
- 內(nèi)存占用
- collect:需要?jiǎng)?chuàng)建中間容器,內(nèi)存占用較高
- reduce:僅維護(hù)累積值,內(nèi)存占用較低
六、實(shí)戰(zhàn)案例對比
1. 統(tǒng)計(jì)員工平均年齡
// 使用 collect
double averageAge = employees.stream()
.collect(Collectors.averagingInt(Employee::getAge));
// 使用 reduce
OptionalDouble averageAge = employees.stream()
.mapToInt(Employee::getAge)
.average();2. 按部門分組員工
// 使用 collect(推薦)
Map<String, List<Employee>> employeesByDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// 使用 reduce(復(fù)雜且低效)
Map<String, List<Employee>> result = employees.stream()
.reduce(
new HashMap<>(),
(map, emp) -> {
map.computeIfAbsent(emp.getDepartment(), k -> new ArrayList<>())
.add(emp);
return map;
},
(m1, m2) -> {
m2.forEach((dept, emps) ->
m1.computeIfAbsent(dept, k -> new ArrayList<>())
.addAll(emps));
return m1;
}
);3. 字符串拼接
// 使用 collect
String result = words.stream()
.collect(Collectors.joining(", "));
// 使用 reduce
String result = words.stream()
.reduce("", (s1, s2) -> s1.isEmpty() ? s2 : s1 + ", " + s2);七、總結(jié)與最佳實(shí)踐
- 優(yōu)先使用 collect:
- 當(dāng)需要生成集合、分組數(shù)據(jù)或執(zhí)行復(fù)雜轉(zhuǎn)換時(shí)
- 在并行流場景中,collect 的性能通常更優(yōu)
- 使用 reduce:
- 當(dāng)需要計(jì)算單個(gè)值(如求和、最大值)時(shí)
- 當(dāng)累積過程不需要中間容器,且可以通過二元操作表達(dá)時(shí)
- 復(fù)雜場景組合使用:
// 先分組,再計(jì)算每組的平均值
Map<String, Double> avgScoreByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.averagingDouble(Student::getScore)
));
- 避免過度使用 reduce:
- 對于復(fù)雜的集合操作,使用 reduce 可能導(dǎo)致代碼冗長且難以維護(hù),應(yīng)優(yōu)先考慮 collect
- 通過合理選擇
collect和reduce,可以使代碼更加簡潔、高效,同時(shí)充分發(fā)揮 Stream API 的優(yōu)勢。
到此這篇關(guān)于Java Stream 的 collect 與 reduce 操作示例詳解的文章就介紹到這了,更多相關(guān)Java Stream 的 collect 與 reduce內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java 集合 Collection介紹及常用方式
- 使用Java Collections實(shí)現(xiàn)集合排序的全面指南
- Java Stream中自定義Collector實(shí)現(xiàn)復(fù)雜數(shù)據(jù)收集的方法
- JAVA中Collections.sort()方法使用詳解
- Java中Collections.sort()排序方法舉例詳解
- Java?Stream?流中?Collectors.toMap?的用法詳解
- 使用Java實(shí)現(xiàn)MapReduce詞頻統(tǒng)計(jì)示例代碼
- Java Stream reduce()使用指南
- 一文帶你掌握java8中的reduce操作
相關(guān)文章
MyBatis源碼解析——獲取SqlSessionFactory方式
這篇文章主要介紹了MyBatis源碼解析——獲取SqlSessionFactory方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
關(guān)于SpringBoot啟動速度慢的原因總結(jié)
這篇文章主要介紹了關(guān)于SpringBoot啟動速度慢的原因總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
Java synchronized關(guān)鍵字和Lock接口實(shí)現(xiàn)原理
這篇文章主要介紹了Java synchronized關(guān)鍵字和Lock接口實(shí)現(xiàn)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
Spring?Cloud?Eureka服務(wù)注冊中心入門流程分析
這篇文章主要介紹了Spring?Cloud?Eureka服務(wù)注冊中心入門流程分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
MyBatis編寫嵌套子查詢的動態(tài)SQL實(shí)踐詳解
在Java生態(tài)中,MyBatis作為一款優(yōu)秀的ORM框架,廣泛應(yīng)用于數(shù)據(jù)庫操作,本文將深入探討如何在MyBatis中編寫嵌套子查詢的動態(tài)SQL,并結(jié)合實(shí)際案例分析其應(yīng)用場景與實(shí)現(xiàn)技巧2025-06-06

