一、核心定位: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());

五、常见误区避坑

  1. 空集合处理不带初始值时,一定要用OptionalifPresent()orElse()处理,避免NullPointerException
  2. 并行流组合器:并行流中组合器的逻辑必须和累加逻辑一致,比如求和的组合器也是Integer::sum
  3. 不可变对象:如果聚合的是不可变对象(比如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,带初始值更省心;
并行流要加组合,线程结果合并好;
求和求积找最大,常用场景记得牢。

更多推荐