Java Lambda之Reduce:从原理到实战记忆指南
·
一、核心定位:Lambda的「聚合神器」
Reduce的本质是把一个集合的所有元素,通过指定的逻辑「聚合」成一个单一结果。
生活类比:把一堆零散的积木,按照你的设计拼成一个完整的模型;把一堆食材,按照食谱做成一道菜。
二、基础语法:3种常用形式
Reduce有3种核心用法,我用「核心公式+代码示例」的形式呈现,方便记忆:
1. 基础版:无初始值(返回Optional)
// 核心公式:集合.reduce((上一次结果, 当前元素) -> 聚合逻辑)
Optional<T> result = stream.reduce((prev, current) -> prev + current);
示例:计算List中所有整数的和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);
// 输出:10
sum.ifPresent(System.out::println);
注意点:如果集合为空,返回Optional.empty(),避免空指针异常。
2. 进阶版:带初始值(直接返回结果)
// 核心公式:集合.reduce(初始值, (上一次结果, 当前元素) -> 聚合逻辑)
T result = stream.reduce(初始值, (prev, current) -> prev + current);
示例:计算List中所有整数的和,初始值为0
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 输出:10
System.out.println(sum);
优势:无需处理Optional,空集合时直接返回初始值(比如0)。
3. 并行版:带组合器(并行流专用)
当使用parallelStream()并行处理时,需要指定「组合器」来合并多个线程的结果:
// 核心公式:集合.reduce(初始值, 累加逻辑, 组合逻辑)
T result = stream.reduce(初始值, (prev, current) -> prev + current, (a, b) -> a + b);
示例:并行计算字符串拼接(避免线程安全问题)
List<String> words = Arrays.asList("I", "love", "Java", "Lambda");
String sentence = words.parallelStream().reduce("",
(prev, current) -> prev + " " + current, // 单个线程内的累加逻辑
(a, b) -> a + b // 多个线程结果的组合逻辑
);
// 输出: I love Java Lambda
System.out.println(sentence.trim());
三、核心知识点记忆清单
我把Reduce的关键规则整理成5条记忆口诀,看完就能记住:
1. 「初始值可选,空集有保障」
- 不带初始值时返回Optional,防止空集合引发空指针
- 带初始值时,空集合直接返回初始值(比如求和初始值0,求积初始值1)
2. 「累加逻辑是核心,两次输入一次出」
- 累加逻辑的Lambda表达式必须接收两个参数(上一次结果+当前元素),返回一个结果
- 比如
(a,b) -> a+b:a是上一次的累加结果,b是当前元素,返回两者的和
3. 「并行流要组合,线程结果要合并」
- 并行流会把集合分成多个子任务,每个子任务单独执行累加逻辑
- 组合器的作用是把多个子任务的结果合并成最终结果
- 非并行流可以省略组合器,但并行流必须指定
4. 「类型要匹配,避免编译错」
- 累加逻辑的返回值类型必须和初始值/集合元素类型一致
- 比如初始值是
0(int),累加逻辑不能返回String
5. 「常用场景记3个,求和求积找最大」
- 求和:
reduce(0, Integer::sum) - 求积:
reduce(1, (a,b) -> a*b) - 找最大值:
reduce(Integer.MIN_VALUE, Integer::max)
四、实战案例:从基础到复杂
1. 基础场景:统计字符串长度总和
List<String> words = Arrays.asList("Hello", "Lambda", "Reduce");
int totalLength = words.stream().reduce(0,
(sum, word) -> sum + word.length(), // 累加每个字符串的长度
Integer::sum // 并行流时合并结果
);
// 输出:16(5+6+5)
System.out.println(totalLength);
2. 复杂场景:自定义对象聚合
比如统计所有用户的总年龄:
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() { return age; }
}
public class ReduceDemo {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("张三", 25),
new User("李四", 30),
new User("王五", 28)
);
// 统计总年龄
int totalAge = users.stream().reduce(0,
(sum, user) -> sum + user.getAge(),
Integer::sum
);
// 输出:83(25+30+28)
System.out.println(totalAge);
}
}
3. 高级场景:自定义聚合逻辑
比如找出年龄最大的用户:
User oldestUser = users.stream().reduce(
new User("", 0), // 初始值:年龄为0的空用户
(prev, current) -> prev.getAge() > current.getAge() ? prev : current,
(a, b) -> a.getAge() > b.getAge() ? a : b
);
// 输出:李四(30岁)
System.out.println(oldestUser.getName());
五、常见误区避坑
- 空集合处理:不带初始值时,一定要用
Optional的ifPresent()或orElse()处理,避免NullPointerException - 并行流组合器:并行流中组合器的逻辑必须和累加逻辑一致,比如求和的组合器也是
Integer::sum - 不可变对象:如果聚合的是不可变对象(比如String),累加逻辑会创建大量中间对象,建议用
StringBuilder代替:
// 错误写法:每次拼接都会创建新String,效率低
String wrong = words.stream().reduce("", (a,b) -> a + b);
// 正确写法:用StringBuilder累加,效率更高
String right = words.stream().reduce(
new StringBuilder(),
(sb, word) -> sb.append(word),
StringBuilder::append
).toString();
六、记忆口诀总结
Reduce聚合三件套,初始累加组合器;
空集返回Optional,带初始值更省心;
并行流要加组合,线程结果合并好;
求和求积找最大,常用场景记得牢。
更多推荐
所有评论(0)