java Stream 详解
Java Stream API 详解
Java Stream API 是 Java 8 引入的一种函数式编程风格的数据处理工具,用于对集合数据进行操作。Stream 提供了一种高效、可读性强的方式来处理数据,支持声明式编程,并利用惰性执行和内部迭代来提升性能。
1. 什么是 Stream?
-
Stream 是什么?
Stream 是对数据集合(如 List、Set、Map)进行处理的抽象层,类似于 SQL 查询中的操作方式。 -
Stream 的特点:
- 声明式:通过链式方法调用定义数据操作,代码简洁。
- 惰性执行:中间操作不会立即执行,只有在终端操作时才会触发执行。
- 无存储:Stream 不存储数据,只是对原始数据的操作视图。
- 不可变性:Stream 本身不会改变原始数据。
- 易并行:支持并行操作,提高性能。
2. Stream 的工作流程
Stream 的操作通常分为三个部分:
- 数据源:通过集合、数组、文件等创建 Stream。
- 中间操作:对数据进行过滤、映射、排序等转换,返回新的 Stream。
- 终端操作:触发数据流处理并生成结果,如收集、遍历或统计。
3. 创建 Stream
3.1 从集合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream(); // 顺序流
Stream<Integer> parallelStream = list.parallelStream(); // 并行流
3.2 从数组
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
3.3 使用静态方法
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
3.4 创建无限流
// 生成无限流,元素为常量
Stream<Integer> stream = Stream.generate(() -> 1);
// 生成无限流,元素为递增序列
Stream<Integer> stream = Stream.iterate(0, n -> n + 2);
4. 中间操作
中间操作是对 Stream 进行的转换操作,不会触发数据处理,而是返回一个新的 Stream。
4.1 过滤操作
filter
:筛选符合条件的元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> filteredStream = list.stream().filter(n -> n % 2 == 0);
4.2 映射操作
map
:将每个元素转换为另一个形式。
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> upperStream = list.stream().map(String::toUpperCase);
flatMap
:将多个流合并为一个流。
List<List<Integer>> lists = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
Stream<Integer> flatStream = lists.stream().flatMap(Collection::stream);
4.3 排序
sorted
:对流中的元素进行排序。
List<Integer> list = Arrays.asList(5, 1, 4, 2);
Stream<Integer> sortedStream = list.stream().sorted();
Stream<Integer> customSortedStream = list.stream().sorted((a, b) -> b - a); // 自定义排序
4.4 去重
distinct
:去除重复元素。
List<Integer> list = Arrays.asList(1, 2, 2, 3, 3);
Stream<Integer> distinctStream = list.stream().distinct();
4.5 截取与跳过
limit(n)
:获取前 n 个元素。skip(n)
:跳过前 n 个元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> limitedStream = list.stream().limit(3); // 1, 2, 3
Stream<Integer> skippedStream = list.stream().skip(2); // 3, 4, 5
5. 终端操作
终端操作会触发 Stream 的实际处理并返回结果。
5.1 收集
collect
:将 Stream 的元素收集到集合、数组等。
List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
Set<String> set = Stream.of("a", "b", "c").collect(Collectors.toSet());
5.2 匹配
allMatch
:是否所有元素都满足条件。anyMatch
:是否存在任意元素满足条件。noneMatch
:是否所有元素都不满足条件。
List<Integer> list = Arrays.asList(1, 2, 3, 4);
boolean allEven = list.stream().allMatch(n -> n % 2 == 0); // false
boolean anyEven = list.stream().anyMatch(n -> n % 2 == 0); // true
5.3 查找
findFirst
:返回第一个元素。findAny
:返回任意一个元素。
Optional<Integer> first = Stream.of(1, 2, 3).findFirst();
Optional<Integer> any = Stream.of(1, 2, 3).findAny();
5.4 迭代
forEach
:遍历元素。
Stream.of("a", "b", "c").forEach(System.out::println);
5.5 统计
count
:统计元素数量。max
/min
:获取最大值或最小值。reduce
:聚合操作。
long count = Stream.of(1, 2, 3).count();
Optional<Integer> max = Stream.of(1, 2, 3).max(Integer::compareTo);
int sum = Stream.of(1, 2, 3).reduce(0, Integer::sum); // 累加
6. 并行流
通过 parallelStream
或 parallel()
可以将流转换为并行流,利用多线程处理数据。
使用并行流
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.parallelStream().forEach(n -> {
System.out.println(Thread.currentThread().getName() + " processes " + n);
});
并行流的注意事项
- 线程安全:需保证数据源和终端操作是线程安全的。
- 性能评估:并行流适合大数据量操作,小数据量可能会因线程切换损耗性能。
7. Stream 的优点
- 简洁:通过链式调用使代码更加简洁易读。
- 高效:惰性执行减少不必要的计算。
- 并行性:内置并行处理支持,充分利用多核 CPU。
- 灵活性:支持多种数据转换和操作。
8. 常见问题与注意事项
-
Stream 只能使用一次:
流一旦被终端操作消费,就无法再次使用。Stream<Integer> stream = Stream.of(1, 2, 3); stream.forEach(System.out::println); stream.forEach(System.out::println); // 报错
-
Stream 不会修改原数据:
Stream 的操作是惰性执行的,不会直接修改数据源。 -
避免过多中间操作:
中间操作过多可能导致性能下降,应合理设计流的处理链。
9. 总结
- Stream 的核心功能:通过中间操作和终端操作,简化集合数据的处理。
- 适用场景:适合对集合数据进行过滤、转换、排序、统计等操作。
- 并行流:充分利用多线程提升性能,但需注意线程安全性。
通过 Stream,可以更优雅地操作数据,提高代码的可读性和开发效率,是现代 Java 编程的重要工具之一。