在Java的Stream API中,归约操作是将集合中的多个元素经过计算合并为单个结果的过程,Collectors.reducing是专门用于实现这类操作的收集器,它提供了三种重载形式,可以适配不同的归约需求,比如数值计算、对象属性合并、自定义规则聚合等场景。

Collectors.reducing的核心重载形式
Collectors.reducing共有三个重载方法,分别对应不同的使用场景,开发者可以根据实际需求选择合适的版本。
1. 单参数形式:reducing(BinaryOperator<T> op)
这种形式接收一个二元操作符,会将流中的元素依次两两合并,最终得到一个Optional<T>类型的结果,适用于不需要初始值、直接对流中所有元素进行归约的场景。
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class ReducingDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和归约,不需要初始值
Optional<Integer> sumOptional = numbers.stream()
.collect(Collectors.reducing((a, b) -> a + b));
// 输出结果:15
sumOptional.ifPresent(System.out::println);
}
}
2. 双参数形式:reducing(T identity, BinaryOperator<T> op)
这种形式除了二元操作符之外,还接收一个初始值identity,当流为空时会直接返回这个初始值,返回结果是T类型而非Optional,适合需要明确默认值的场景。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ReducingDemo2 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和,初始值为0,流为空时返回0
Integer sum = numbers.stream()
.collect(Collectors.reducing(0, (a, b) -> a + b));
// 输出结果:15
System.out.println(sum);
List<Integer> emptyList = Arrays.asList();
Integer emptySum = emptyList.stream()
.collect(Collectors.reducing(0, (a, b) -> a + b));
// 输出结果:0
System.out.println(emptySum);
}
}
3. 三参数形式:reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)
这种形式增加了映射函数mapper,会在归约前先将流中的元素转换为指定类型,再进行归约操作,适合需要对元素先处理再聚合的场景,比如对象集合中提取某个属性再计算。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public Integer getAge() {
return age;
}
}
public class ReducingDemo3 {
public static void main(String[] args) {
List<User> userList = Arrays.asList(
new User("张三", 20),
new User("李四", 25),
new User("王五", 22)
);
// 先提取年龄,再求和,初始值为0
Integer totalAge = userList.stream()
.collect(Collectors.reducing(
0, // 初始值
User::getAge, // 映射函数,提取年龄
(a, b) -> a + b // 归约规则,求和
));
// 输出结果:67
System.out.println(totalAge);
}
}
Collectors.reducing的使用注意事项
- 归约操作需要满足结合律,否则多线程并行流下执行结果会和预期不一致,比如减法操作就不符合结合律,不建议使用reducing做减法归约。
- 单参数形式的reducing在流为空时会返回Optional.empty(),使用时需要做空值判断,避免空指针异常。
- 三参数形式的映射函数如果返回null,会导致归约过程出现异常,需要确保映射逻辑不会返回null值。
与其他归约方法的对比
Stream API中除了Collectors.reducing,还有Stream.reduce()方法也可以实现归约,两者的核心逻辑一致,但是使用场景略有不同:
| 对比项 | Collectors.reducing | Stream.reduce() |
|---|---|---|
| 适用场景 | 作为收集器用在collect()方法中,适合和其他收集器组合使用 | 直接用在Stream流上,适合单独的归约操作 |
| 返回值 | 单参数返回Optional,双/三参数返回具体类型 | 单参数返回Optional,双/三参数返回具体类型 |
| 组合性 | 可以方便地和groupingBy等收集器组合实现分组归约 | 不支持直接和其他收集器组合 |
如果需要先对集合分组再对每组做归约,使用Collectors.reducing配合Collectors.groupingBy会更简洁:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Product {
private String category;
private Double price;
public Product(String category, Double price) {
this.category = category;
this.price = price;
}
public String getCategory() {
return category;
}
public Double getPrice() {
return price;
}
}
public class ReducingDemo4 {
public static void main(String[] args) {
List<Product> productList = Arrays.asList(
new Product("电子", 1999.0),
new Product("电子", 2999.0),
new Product("图书", 59.0),
new Product("图书", 89.0)
);
// 按分类分组,计算每类商品的总价
Map<String, Double> categoryTotalPrice = productList.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.reducing(
0.0,
Product::getPrice,
(a, b) -> a + b
)
));
// 输出结果:{电子=4998.0, 图书=148.0}
System.out.println(categoryTotalPrice);
}
}
实际开发中,如果是简单的数值归约,比如求和、求最大值,也可以直接使用Collectors.summingInt、Collectors.maxBy等专用收集器,代码可读性会更高,reducing更适合自定义归约规则的场景。
Collectors.reducingJava集合归约Stream_API修改时间:2026-07-19 03:36:36