一、核心定位: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,带初始值更省心; 并行流要加组合,线程结果合并好; 求和求积找最大,常用场景记得牢。