Java Stream API 完全指南

目录

  1. Stream 核心概念
  2. 中间操作(Intermediate Operations)​
  3. 终端操作(Terminal Operations)​
  4. 并行流与性能优化
  5. 使用场景与最佳实践
  6. 常见问题与陷阱

1. Stream 核心概念

1.1 什么是 Stream?

  • 定义:表示元素序列的抽象数据管道,支持函数式编程风格的操作。
  • 特性
    • 不存储数据:仅传递数据源(集合、数组、I/O)的元素。
    • 不可变:操作生成新 Stream,不修改源数据。
    • 延迟执行:中间操作在终端操作触发时执行。
    • 可消费性:Stream 只能被操作一次。

1.2 创建 Stream

方法 示例
集合stream() / parallelStream() list.stream()
数组Arrays.stream() Arrays.stream(new int[]{1,2,3})
值序列Stream.of() Stream.of("a", "b", "c")
文件Files.lines() Files.lines(Path.of("file.txt"))
无限流generate() / iterate() Stream.generate(Math::random).limit(5)

2. 中间操作(Intermediate Operations)

中间操作返回新 Stream,支持链式调用。以下是完整分类:

2.1 筛选与切片

操作 描述 示例
filter(Predicate) 过滤满足条件的元素 .filter(n -> n % 2 == 0)
distinct() 去重(依赖 equals() .distinct()
limit(n) 截取前 n 个元素 .limit(3)
skip(n) 跳过前 n 个元素 .skip(2)
takeWhile(Predicate) (Java 9+) 取元素直到条件不满足 .takeWhile(n -> n < 5)
dropWhile(Predicate) (Java 9+) 丢弃元素直到条件不满足 .dropWhile(n -> n < 5)

2.2 映射与转换

操作 描述 示例
map(Function) 元素一对一转换 .map(s -> s.toUpperCase())
flatMap(Function) 元素一对多转换并扁平化 .flatMap(s -> Stream.of(s.split(" ")))
peek(Consumer) 观察元素(调试用) .peek(System.out::println)

2.3 排序与状态

操作 描述 示例
sorted() 自然排序 .sorted()
sorted(Comparator) 自定义排序规则 .sorted(Comparator.reverseOrder())
unordered() 声明无需顺序(优化并行性能) .unordered()

3. 终端操作(Terminal Operations)

终端操作触发流处理,返回非流结果。

3.1 搜索与匹配

操作 描述 示例
anyMatch(Predicate) 是否存在至少一个元素满足条件 .anyMatch(s -> s.contains("a"))
allMatch(Predicate) 是否所有元素满足条件 .allMatch(s -> s.length() > 2)
noneMatch(Predicate) 是否没有元素满足条件 .noneMatch(s -> s.startsWith("z"))
findFirst() 返回第一个元素(保证顺序) .findFirst()
findAny() 返回任意元素(并行流优化) .findAny()

3.2 聚合与收集

操作 描述 示例
count() 返回元素总数 .count()
collect(Collector) 转换为集合或其他数据结构 .collect(Collectors.toList())
reduce() 归约操作(如求和、最大值) .reduce(0, Integer::sum)
forEach(Consumer) 遍历元素(无返回值) .forEach(System.out::println)
toArray() 转换为数组 .toArray(String[]::new)

3.3 数值流特化(避免装箱开销)

操作 描述 示例
sum() 求和(仅限数值流) IntStream.of(1,2,3).sum()
average() 平均值 IntStream.range(1,5).average()
max() / min() 最大值/最小值 IntStream.of(1,2,3).max()

4. 并行流与性能优化

4.1 启用并行流


 
java
复制
List<Integer> list = Arrays.asList(1, 2, 3);
Stream<Integer> parallelStream = list.parallelStream(); // 集合方式
Stream<Integer> parallel = Stream.of(1,2,3).parallel(); // 显式转换

4.2 并行流注意事项

  1. 线程安全:确保操作无状态且无副作用。
  2. 避免有状态中间操作:如 sorteddistinct 可能降低性能。
  3. 短路操作优化limitfindFirst 在有序流中可能串行更高效。

4.3 性能测试工具


 
java
复制
long start = System.currentTimeMillis();
// 执行流操作
long duration = System.currentTimeMillis() - start;
System.out.println("耗时:" + duration + "ms");

5. 使用场景与最佳实践

5.1 典型场景

场景 示例操作链
数据过滤与转换 .filter(...).map(...).collect(...)
统计与聚合 .mapToInt(...).average().getAsDouble()
分页查询 .skip((page-1)*size).limit(size).collect(...)
条件检查 .anyMatch(...)  .noneMatch(...)

5.2 最佳实践

  1. 优先使用方法引用String::toUpperCase 替代 s -> s.toUpperCase()
  2. 合并过滤条件:将多个 filter 合并为一个复合谓词。
  3. 避免副作用:不要在 peek  map 中修改外部状态。
  4. 谨慎使用并行流:数据量大且无状态操作时启用。

6. 常见问题与陷阱

6.1 流已被操作或关闭


 
java
复制
Stream<String> stream = Stream.of("a", "b");
stream.forEach(System.out::println);
stream.count(); // 抛出 IllegalStateException

6.2 无限流未限制


 
java
复制
Stream.generate(() -> "data").forEach(System.out::println); // 无限循环

6.3 副作用导致的并发问题


 
java
复制
List<Integer> sharedList = new ArrayList<>();
IntStream.range(0, 1000).parallel().forEach(sharedList::add); // 非线程安全!

6.4 装箱/拆箱开销


 
java
复制
// 错误:使用 Stream<Integer> 导致频繁装箱
Stream.of(1,2,3).reduce(0, Integer::sum);
// 正确:使用 IntStream
IntStream.of(1,2,3).sum();

附录:常用 Collectors 工具方法

方法 描述 示例
toList() / toSet() 转换为 List/Set .collect(Collectors.toList())
toMap(keyMapper, valueMapper) 转换为 Map .collect(Collectors.toMap(k -> k, v -> v))
joining(delimiter) 字符串拼接 .collect(Collectors.joining(", "))
groupingBy(classifier) 按条件分组 .collect(Collectors.groupingBy(User::getAge))
partitioningBy(predicate) 按布尔条件分区 .collect(Collectors.partitioningBy(s -> s.length() > 5))