后端开发

Java Stream Transform:流式数据处理的艺术

2024-11-10 wangjun 12 min read
{}

Stream 是什么?

Java 8 引入的 Stream API 是对集合操作的声明式抽象。它不是数据结构,而是一套描述数据 Transform 管道 的接口。核心思想是:数据经过一系列中间操作(map/filter/sorted)的变换,最终由终止操作(collect/reduce/forEach)触发执行。

三种核心 Transform 操作

1. map -- 元素映射

map 是最纯粹的 Transform:对每个元素应用函数,产生新元素。

List<String> names = List.of("alice", "bob", "charlie");
List<String> upper = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());


2. flatMap -- 扁平化映射

当映射结果本身是流时,flatMap 将多个流合并为一个:

List<List<Integer>> nested = List.of(
    List.of(1, 2), List.of(3, 4), List.of(5));
List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());


3. reduce -- 归约聚合

reduce 将流中的元素反复结合,最终归约为一个值:

int sum = IntStream.range(1, 101)
    .reduce(0, Integer::sum);


懒求值机制

Stream 的中间操作是 惰性 的 -- 它们不会立即执行,直到遇到终止操作。这使得无限流成为可能:

// 生成前 10 个偶数的平方
List<Integer> squares = IntStream.iterate(1, n -> n + 1)
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .limit(10)
    .boxed()
    .collect(Collectors.toList());

如果没有 limitcollect 会永远等待。正是因为懒求值,filtermap 不会提前处理所有元素。

并行流 Parallel Stream

只需将 stream() 改为 parallelStream(),框架自动利用 ForkJoinPool 进行并行处理:

long count = list.parallelStream()
    .filter(x -> x > 100)
    .count();

但要注意:并行不总是更快。对于小数据集或 IO 密集操作,线程切换的开销可能超过收益。

Collector 的组合艺术

Collectors.groupingBy 结合 mappingreducing 可以构建强大的分组聚合:

// 按部门分组,求每个部门的平均薪资
Map<String, Double> avgSalary = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDept,
        Collectors.averagingDouble(Employee::getSalary)));


Map<String, Optional<Employee>> topEarner = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDept,
        Collectors.maxBy(Comparator.comparing(Employee::getSalary))));
Stream 的本质是函数式编程中的 Transform 组合 -- 你描述"做什么",而不是"怎么做",让框架去优化"怎么做"。