java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java Stream 的 collect 与 reduce

Java Stream 的 collect 与 reduce 操作示例详解

作者:潜意识Java

在 Java Stream API 中,collect 和 reduce 是两种强大的终止操作,用于将流中的元素累积为最终结果,本文将从核心概念、使用场景、性能特性等多个维度进行对比分析,感兴趣的朋友跟随小编一起看看吧

在 Java Stream API 中,collect 和 reduce 是两种强大的终止操作,用于将流中的元素累积为最终结果。尽管它们都用于聚合数据,但实现机制和应用场景存在显著差异。本文将从核心概念、使用场景、性能特性等多个维度进行对比分析。

一、核心概念对比

特性collect(Collector)reduce(BinaryOperator)
操作类型可变容器汇聚操作不可变值累积操作
结果类型可变容器(如 List、Set、Map)单个值或容器(需通过 Supplier 创建)
数据结构支持中途修改的集合不可变对象的累积
并行处理效率高(可并发操作独立容器后合并)低(需按顺序累积或复杂 combiner)
典型应用场景数据分组、分区、转换集合类型求和、求最大值、字符串拼接

二、collect 方法详解

collect 是一种可变容器汇聚操作,通过 Collector 接口实现复杂的归约逻辑。它将流中的元素累积到一个可变容器(如 List、Set、Map)中,并支持进一步的处理。

1. 基础用法示例

// 将 Stream 转换为 List
List<String> names = Stream.of("Alice", "Bob", "Charlie")
    .collect(Collectors.toList());
// 将 Stream 转换为 Set 去重
Set<Integer> uniqueNumbers = Stream.of(1, 2, 2, 3, 3, 3)
    .collect(Collectors.toSet());
// 使用 toCollection 指定具体集合类型
LinkedList<String> linkedList = Stream.of("a", "b", "c")
    .collect(Collectors.toCollection(LinkedList::new));

2. 高级用法:分组与分区

class Person {
    private String name;
    private int age;
    private String city;
    // 构造器、getter 略
}
// 按城市分组
Map<String, List<Person>> peopleByCity = personStream
    .collect(Collectors.groupingBy(Person::getCity));
// 按年龄是否大于18分区
Map<Boolean, List<Person>> partitionByAge = personStream
    .collect(Collectors.partitioningBy(p -> p.getAge() > 18));

3. 自定义 Collector

// 自定义 Collector 实现字符串拼接
Collector<String, StringBuilder, String> stringCollector = Collector.of(
    StringBuilder::new,                  // 创建容器
    StringBuilder::append,               // 累积元素
    (sb1, sb2) -> sb1.append(sb2),       // 合并容器
    StringBuilder::toString              // 最终转换
);
String result = Stream.of("Hello", " ", "World")
    .collect(stringCollector);  // 输出:Hello World

三、reduce 方法详解

reduce 是一种不可变值累积操作,通过二元操作(BinaryOperator)将流中的元素依次处理,最终得到一个值。

1. 基础用法示例

// 无初始值的 reduce(返回 Optional)
Optional<Integer> sum = Stream.of(1, 2, 3, 4)
    .reduce(Integer::sum);  // 结果:10
// 有初始值的 reduce
int product = Stream.of(1, 2, 3, 4)
    .reduce(1, (a, b) -> a * b);  // 结果:24
// 复杂累积:字符串拼接
String concatenated = Stream.of("a", "b", "c")
    .reduce("", String::concat);  // 结果:abc

2. 并行流中的 reduce

// 并行流中使用带 combiner 的 reduce
int sum = Stream.of(1, 2, 3, 4)
    .parallel()
    .reduce(0,  // 初始值
        Integer::sum,  // 累积器
        Integer::sum); // 合并器(并行流必须)

3. 自定义累积逻辑

// 计算最大值
Optional<Integer> max = Stream.of(5, 3, 9, 1)
    .reduce((a, b) -> a > b ? a : b);  // 结果:9
// 计算平均值(需要自定义累积器)
class Average {
    private int sum;
    private int count;
    public double get() {
        return count > 0 ? (double) sum / count : 0;
    }
}
Average average = Stream.of(1, 2, 3, 4, 5)
    .reduce(new Average(),
        (avg, num) -> {
            avg.sum += num;
            avg.count++;
            return avg;
        },
        (avg1, avg2) -> {
            avg1.sum += avg2.sum;
            avg1.count += avg2.count;
            return avg1;
        });
double result = average.get();  // 结果:3.0

四、核心区别与选择策略

场景collect 更适合reduce 更适合
结果类型集合或复杂对象单个值(如数字、字符串)
并行处理效率高(独立容器可并发合并)低(需顺序累积或复杂 combiner)
累积过程是否可变可变(操作集合内部元素)不可变(生成新对象)
是否需要分组 / 分区是(groupingBy、partitioningBy)
是否需要多阶段处理是(Collector 链)

五、性能对比与优化建议

六、实战案例对比

1. 统计员工平均年龄

// 使用 collect
double averageAge = employees.stream()
    .collect(Collectors.averagingInt(Employee::getAge));
// 使用 reduce
OptionalDouble averageAge = employees.stream()
    .mapToInt(Employee::getAge)
    .average();

2. 按部门分组员工

// 使用 collect(推荐)
Map<String, List<Employee>> employeesByDepartment = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment));
// 使用 reduce(复杂且低效)
Map<String, List<Employee>> result = employees.stream()
    .reduce(
        new HashMap<>(),
        (map, emp) -> {
            map.computeIfAbsent(emp.getDepartment(), k -> new ArrayList<>())
               .add(emp);
            return map;
        },
        (m1, m2) -> {
            m2.forEach((dept, emps) -> 
                m1.computeIfAbsent(dept, k -> new ArrayList<>())
                  .addAll(emps));
            return m1;
        }
    );

3. 字符串拼接

// 使用 collect
String result = words.stream()
    .collect(Collectors.joining(", "));
// 使用 reduce
String result = words.stream()
    .reduce("", (s1, s2) -> s1.isEmpty() ? s2 : s1 + ", " + s2);

七、总结与最佳实践

// 先分组,再计算每组的平均值
Map<String, Double> avgScoreByClass = students.stream()
    .collect(Collectors.groupingBy(
        Student::getClassName,
        Collectors.averagingDouble(Student::getScore)
    ));

到此这篇关于Java Stream 的 collect 与 reduce 操作示例详解的文章就介绍到这了,更多相关Java Stream 的 collect 与 reduce内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文