Java Stream归约
外观
Java Stream归约(Reduction)是Java Stream API中的核心操作之一,它允许将流中的元素通过特定规则合并为一个结果。归约操作广泛应用于数据聚合、统计和转换场景,是函数式编程的重要概念。
概述[编辑 | 编辑源代码]
归约操作通过组合流中的元素,逐步生成最终结果。常见的归约操作包括求和、求最大值、字符串拼接等。Java Stream API提供了两种主要归约方式:
- 终端操作:如、
reduce()
、collect()
、min()
等max()
- 可变归约:通过工具类实现更复杂的聚合
Collectors
基本归约操作[编辑 | 编辑源代码]
reduce()方法[编辑 | 编辑源代码]
reduce()
是Stream API中最基础的归约方法,有三种重载形式:
// 1. 无初始值版本
Optional<T> reduce(BinaryOperator<T> accumulator)
// 2. 有初始值版本
T reduce(T identity, BinaryOperator<T> accumulator)
// 3. 并行流优化版本
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
示例1:求和[编辑 | 编辑源代码]
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 无初始值
Optional<Integer> sum1 = numbers.stream().reduce((a, b) -> a + b);
System.out.println(sum1.get()); // 输出: 15
// 有初始值
int sum2 = numbers.stream().reduce(0, (a, b) -> a + b);
System.out.println(sum2); // 输出: 15
示例2:求最大值[编辑 | 编辑源代码]
Optional<Integer> max = numbers.stream().reduce(Integer::max);
System.out.println(max.get()); // 输出: 5
可变归约(Collectors)[编辑 | 编辑源代码]
对于更复杂的归约操作,可以使用
Collectors
工具类:
常用收集器[编辑 | 编辑源代码]
方法 | 描述 | 示例 |
---|---|---|
toList()
|
收集到List | stream.collect(Collectors.toList())
|
joining()
|
字符串拼接 | stream.collect(Collectors.joining(", "))
|
summingInt()
|
整数求和 | stream.collect(Collectors.summingInt(x -> x))
|
averagingDouble()
|
求平均值 | stream.collect(Collectors.averagingDouble(x -> x))
|
示例:复杂归约[编辑 | 编辑源代码]
List<String> words = Arrays.asList("Java", "Stream", "API");
// 字符串拼接
String joined = words.stream()
.collect(Collectors.joining(", "));
System.out.println(joined); // 输出: Java, Stream, API
// 分组统计
Map<Integer, List<String>> lengthMap = words.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(lengthMap); // 输出: {4=[Java], 6=[Stream, API]}
并行归约[编辑 | 编辑源代码]
Stream API自动支持并行归约,但需要注意:
- 累加器(accumulator)和组合器(combiner)必须满足结合律
- 初始值(identity)必须满足:combiner.apply(identity, x) = x
并行示例[编辑 | 编辑源代码]
int parallelSum = numbers.parallelStream()
.reduce(0, (a, b) -> a + b, Integer::sum);
System.out.println(parallelSum); // 输出: 15
实际应用案例[编辑 | 编辑源代码]
案例1:电商订单统计[编辑 | 编辑源代码]
计算订单总金额和平均金额:
class Order {
String id;
double amount;
// 构造方法/getters省略
}
List<Order> orders = Arrays.asList(
new Order("A001", 99.99),
new Order("A002", 149.99),
new Order("A003", 79.99)
);
DoubleSummaryStatistics stats = orders.stream()
.collect(Collectors.summarizingDouble(Order::getAmount));
System.out.println("总数: " + stats.getCount());
System.out.println("总和: " + stats.getSum());
System.out.println("平均: " + stats.getAverage());
System.out.println("最大: " + stats.getMax());
System.out.println("最小: " + stats.getMin());
案例2:日志分析[编辑 | 编辑源代码]
统计不同日志级别的出现次数:
List<String> logs = Arrays.asList(
"ERROR: Disk full",
"WARN: Connection timeout",
"ERROR: File not found",
"INFO: User logged in"
);
Map<String, Long> levelCounts = logs.stream()
.map(log -> log.split(":")[0])
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
System.out.println(levelCounts);
// 输出: {ERROR=2, WARN=1, INFO=1}
性能考虑[编辑 | 编辑源代码]
- 对于简单归约,通常比
reduce()
更高效collect()
- 复杂归约应优先使用
Collectors
- 大数据集考虑使用并行流,但要注意线程安全问题
数学基础[编辑 | 编辑源代码]
归约操作在数学上可以表示为:
其中f是满足结合律的二元操作。
常见问题[编辑 | 编辑源代码]
何时使用reduce() vs collect()?[编辑 | 编辑源代码]
- 使用当:
reduce()
* 操作是不可变的(如数值计算) * 不需要中间容器
- 使用当:
collect()
* 需要可变容器(如集合) * 操作涉及状态累积
为什么Optional在无初始值的reduce()中返回?[编辑 | 编辑源代码]
因为空流没有有效返回值,Optional明确表示了这种可能为空的情况。
总结[编辑 | 编辑源代码]
Java Stream归约提供了强大的数据聚合能力:
- 简单归约使用
reduce()
- 复杂归约使用
Collectors
- 并行流可提高大数据集处理效率
- 遵循函数式编程原则,写出更声明式的代码
通过合理应用归约操作,可以显著简化集合处理代码,提高程序可读性和维护性。