Stream
是 Java 8 引入的一个强大的功能,用于处理集合(Collection)或数组中的数据。它提供了一种声明式的编程方式,可以极大地简化对数据的操作,例如过滤、排序、映射和聚合等。
1. 什么是 Stream 流?
- 定义:
Stream
是一个来自数据源(如集合、数组等)的元素序列,支持顺序和并行操作。 - 特点:
- 惰性求值(Lazy Evaluation):Stream 的中间操作不会立即执行,只有在终端操作触发时才会真正执行。
- 不可变性:Stream 操作不会修改原始数据源,而是生成新的流或结果。
- 链式调用:多个操作可以通过链式调用的方式组合在一起。
2. Stream 的工作流程
使用 Stream 流通常分为以下三个步骤:
- 创建流:从数据源(如集合、数组)创建一个 Stream。
- 中间操作:对流中的数据进行一系列操作(如过滤、映射、排序等),返回一个新的流。
- 终端操作:触发流的执行,并生成最终结果(如收集到集合、计算总数等)。
3. 创建 Stream
可以通过多种方式创建 Stream:
(1)从集合创建
List<String> list = Arrays.asList("A", "B", "C"); Stream<String> stream = list.stream(); // 创建串行流 Stream<String> parallelStream = list.parallelStream(); // 创建并行流
(2)从数组创建
String[] array = {"A", "B", "C"}; Stream<String> stream = Arrays.stream(array);
(3)使用静态方法
// 创建一个包含指定元素的流 Stream<String> stream = Stream.of("A", "B", "C");// 创建一个空流 Stream<String> emptyStream = Stream.empty();
4. 中间操作
中间操作是对流中的数据进行处理,返回一个新的流。常见的中间操作包括:
(1)过滤(filter
)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 过滤出偶数 Stream<Integer> filteredStream = numbers.stream().filter(n -> n % 2 == 0);
(2)映射(map
)
List<String> words = Arrays.asList("apple", "banana", "cherry");// 将每个单词转换为大写 Stream<String> upperCaseStream = words.stream().map(String::toUpperCase);
(3)排序(sorted
)
List<Integer> numbers = Arrays.asList(5, 3, 8, 1);// 升序排序 Stream<Integer> sortedStream = numbers.stream().sorted();// 自定义排序规则 Stream<Integer> customSortedStream = numbers.stream().sorted(Comparator.reverseOrder());
(4)去重(distinct
)
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4);// 去重后的流 Stream<Integer> distinctStream = numbers.stream().distinct();
(5)限制和跳过(limit
和 skip
)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 取前 3 个元素 Stream<Integer> limitedStream = numbers.stream().limit(3);// 跳过前 2 个元素 Stream<Integer> skippedStream = numbers.stream().skip(2);
5. 终端操作
终端操作会触发流的执行,并生成最终结果。常见的终端操作包括:
(1)收集结果(collect
)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 收集为 List List<Integer> result = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());// 收集为 Set Set<Integer> resultSet = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toSet());
(2)统计信息(count
、min
、max
等)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 统计元素个数 long count = numbers.stream().count();// 查找最大值 Optional<Integer> max = numbers.stream().max(Integer::compareTo);// 查找最小值 Optional<Integer> min = numbers.stream().min(Integer::compareTo);
(3)遍历(forEach
)
List<String> words = Arrays.asList("apple", "banana", "cherry");// 打印每个单词 words.stream().forEach(System.out::println);
(4)判断条件(anyMatch
、allMatch
、noneMatch
)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 是否存在偶数 boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0);// 是否所有元素都是偶数 boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);// 是否没有偶数 boolean noEven = numbers.stream().noneMatch(n -> n % 2 == 0);
6. 并行流
Java 8 提供了并行流(Parallel Stream),可以利用多核 CPU 并行处理数据:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 使用并行流计算总和 int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
- 优点:对于大数据量的操作,可以显著提高性能。
- 缺点:并行流可能会引入线程安全问题,且不一定总是比串行流快。
7. Stream 的注意事项
- 一次性消费:Stream 流只能被消费一次,再次使用需要重新创建。
- 避免副作用:尽量避免在 Stream 操作中修改外部状态,保持函数式编程的纯函数特性。
- 性能优化:合理选择中间操作和终端操作,避免不必要的复杂操作。
8. 总结
Stream 是 Java 8 中非常重要的特性,能够极大地简化集合操作。它的主要优势包括:
- 简洁性:通过链式调用实现复杂的操作逻辑。
- 可读性:代码更加直观,易于理解。
- 高效性:支持并行流,充分利用多核 CPU 性能。
如果你需要处理集合或数组中的数据,推荐优先考虑使用 Stream 流来实现