Java Stream API 深度解析:从入门到高阶应用
Stream API 是 Java 8 引入的核心特性之一,它提供了一种高效、声明式的方式处理集合数据。本文将从基础概念讲起,逐步深入,涵盖 Stream 的创建、中间操作、终端操作、并行流、性能优化 等核心内容,并结合实际代码示例帮助你彻底掌握 Stream。
1. Stream 基础概念
1.1 什么是 Stream?
Stream(流)是 数据元素的序列,支持顺序或并行聚合操作。它的核心特点:
- 不是数据结构,不存储数据,而是对数据源(集合、数组、I/O 等)进行计算。
- 惰性求值(Lazy Evaluation):中间操作不会立即执行,只有终端操作触发时才会处理数据。
- 不可复用:一旦流被消费(终端操作执行),就不能再使用。
1.2 Stream 操作分类
类型 | 方法示例 | 说明 |
---|---|---|
中间操作 | filter() , map() , distinct() , sorted() | 返回新 Stream,可链式调用 |
终端操作 | forEach() , collect() , reduce() , count() | 触发计算,返回非 Stream 结果 |
2. Stream 的创建方式
2.1 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream(); // 顺序流
Stream<String> parallelStream = list.parallelStream(); // 并行流
2.2 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
2.3 使用 Stream.of()
Stream<String> stream = Stream.of("a", "b", "c");
2.4 生成无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1); // 0, 1, 2, ...
Stream<Double> randomStream = Stream.generate(Math::random); // 随机数流
3. 核心中间操作详解
3.1 filter(Predicate)
:过滤元素
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0) // 只保留偶数.collect(Collectors.toList()); // [2, 4]
3.2 map(Function)
:元素转换
List<String> names = Arrays.asList("Alice", "Bob");
List<Integer> nameLengths = names.stream().map(String::length) // 转为名字长度.collect(Collectors.toList()); // [5, 3]
3.3 flatMap(Function)
:扁平化流
List<List<String>> nestedList = Arrays.asList(Arrays.asList("a", "b"),Arrays.asList("c", "d")
);
List<String> flatList = nestedList.stream().flatMap(Collection::stream) // 合并子流.collect(Collectors.toList()); // ["a", "b", "c", "d"]
3.4 distinct()
:去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3);
List<Integer> uniqueNumbers = numbers.stream().distinct().collect(Collectors.toList()); // [1, 2, 3]
3.5 sorted()
:排序
List<String> names = Arrays.asList("Bob", "Alice");
List<String> sortedNames = names.stream().sorted() // 自然排序.collect(Collectors.toList()); // ["Alice", "Bob"]
4. 核心终端操作详解
4.1 forEach(Consumer)
:遍历
List<String> names = Arrays.asList("Alice", "Bob");
names.stream().forEach(System.out::println);
4.2 collect(Collector)
:收集结果
List<String> names = Arrays.asList("a", "b", "c");
Set<String> nameSet = names.stream().collect(Collectors.toSet()); // 转为 Set
4.3 reduce()
:归约计算
List<Integer> numbers = Arrays.asList(1, 2, 3);
int sum = numbers.stream().reduce(0, (a, b) -> a + b); // 求和,输出 6
4.4 anyMatch()
/ allMatch()
/ noneMatch()
List<Integer> numbers = Arrays.asList(1, 2, 3);
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0); // 是否有偶数? true
5. 并行流(Parallel Stream)
5.1 创建并行流
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
int sum = numbers.parallelStream() // 并行计算.mapToInt(Integer::intValue).sum();
5.2 注意事项
- 线程安全问题:避免共享可变状态。
- 数据量小可能更慢:并行流有线程切换开销。
6. 性能优化技巧
- 优先使用基本类型流(
IntStream
、LongStream
)避免装箱开销。 - 短路操作优化:
findFirst()
比findAny()
更高效(顺序流)。 - 避免复杂链式操作:可拆分为多个 Stream 操作。
7. 实际应用案例
案例 1:统计单词频率
List<String> words = Arrays.asList("apple", "banana", "apple");
Map<String, Long> wordCount = words.stream().collect(Collectors.groupingBy(Function.identity(),Collectors.counting())); // {"apple": 2, "banana": 1}
案例 2:Excel 数据处理(Apache POI)
List<Row> rows = IntStream.rangeClosed(1, sheet.getLastRowNum()).mapToObj(sheet::getRow).filter(Objects::nonNull).collect(Collectors.toList());
8. 总结
核心要点 | 说明 |
---|---|
Stream 是惰性求值的 | 只有终端操作触发计算 |
中间操作返回新 Stream | 可链式调用 |
并行流需谨慎使用 | 注意线程安全 |
优先使用基本类型流 | 提升性能 |
Stream API 让 Java 集合操作变得更简洁、高效,适合大数据处理和函数式编程。掌握它,能极大提升代码质量和开发效率! 🚀
推荐练习:尝试用 Stream 实现以下功能:
- 找出列表中的最大值。
- 将
List<String>
按长度分组。 - 从 100 万数字中筛选偶数并求和(比较并行流与顺序流性能)。