关于Stream
在 Java 中,Stream API 是 Java 8 引入的一个强大工具,用于处理集合(Collection)中的元素。它提供了一种高效且声明式的方式来处理数据,支持过滤、映射、聚合等操作。Stream 操作可以分为两类:中间操作(Intermediate Operations) 和 终止操作(Terminal Operations)。
Stream 的基本概念
- 流(Stream):不是一个数据结构,而是一个来自数据源的元素队列并支持聚合操作。
- 数据源:可以是集合、数组、I/O 通道等。
- 聚合操作:如 filter、map、reduce、find、match 等。
- 特点:
- 不存储数据:流只是对数据源的元素进行计算。
- 一次性使用:流只能遍历一次,遍历后就会关闭。
- 延迟执行:中间操作不会立即执行,只有在终止操作时才会触发。
中间操作(Intermediate Operations)
中间操作会返回一个新的流,允许你链式调用多个操作。常见的中间操作包括:
1. 过滤(Filtering)
filter(Predicate<T>)
:过滤出满足条件的元素。List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); names.stream().filter(name -> name.length() > 4) // 保留长度大于4的名字.forEach(System.out::println); // 输出:Alice, Charlie
2. 映射(Mapping)
map(Function<T, R>)
:将元素转换为另一种类型。List<Integer> lengths = names.stream().map(String::length) // 将每个字符串映射为其长度.collect(Collectors.toList()); // [5, 3, 7, 5]
flatMap(Function<T, Stream<R>>)
:将每个元素的流合并为一个流。List<List<Integer>> nestedList = Arrays.asList(Arrays.asList(1, 2),Arrays.asList(3, 4) ); List<Integer> flattenedList = nestedList.stream().flatMap(List::stream) // 将嵌套列表展平.collect(Collectors.toList()); // [1, 2, 3, 4]
3. 排序(Sorting)
sorted()
:自然排序。sorted(Comparator<T>)
:自定义排序。names.stream().sorted() // 按字母顺序排序.forEach(System.out::println);names.stream().sorted(Comparator.comparingInt(String::length)) // 按长度排序.forEach(System.out::println);
4. 去重(Distinct)
distinct()
:根据元素的equals()
方法去重。List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3); numbers.stream().distinct() // 去重.forEach(System.out::println); // 输出:1, 2, 3
5. 截断(Limiting)和跳过(Skipping)
limit(long n)
:截取前 n 个元素。skip(long n)
:跳过前 n 个元素。names.stream().limit(2) // 只取前两个元素.forEach(System.out::println); // 输出:Alice, Bobnames.stream().skip(2) // 跳过前两个元素.forEach(System.out::println); // 输出:Charlie, David
6. ** peek**
peek(Consumer<T>)
:对每个元素执行操作,但不改变元素,常用于调试。names.stream().filter(name -> name.length() > 4).peek(name -> System.out.println("Filtered name: " + name)) // 调试输出.map(String::toUpperCase).forEach(System.out::println);
终止操作(Terminal Operations)
终止操作会消耗流,产生一个结果或副作用。常见的终止操作包括:
1. 遍历(ForEach)
forEach(Consumer<T>)
:对每个元素执行操作。names.stream().forEach(name -> System.out.println("Hello, " + name));
2. 收集(Collect)
collect(Collector<T, A, R>)
:将元素收集到集合或其他数据结构中。List<String> filteredNames = names.stream().filter(name -> name.startsWith("A")).collect(Collectors.toList());Map<Integer, List<String>> namesByLength = names.stream().collect(Collectors.groupingBy(String::length));
3. 聚合(Reduce)
reduce(T identity, BinaryOperator<T>)
:将元素组合成一个值。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream().reduce(0, Integer::sum); // 计算总和:15
4. 匹配(Matching)
anyMatch(Predicate<T>)
:是否至少有一个元素匹配。allMatch(Predicate<T>)
:是否所有元素都匹配。noneMatch(Predicate<T>)
:是否没有元素匹配。boolean hasLongName = names.stream().anyMatch(name -> name.length() > 5); // true boolean allUpperCase = names.stream().allMatch(name -> name.equals(name.toUpperCase())); // false
5. 查找(Finding)
findFirst()
:返回第一个元素。findAny()
:返回任意一个元素(并行流中更有用)。Optional<String> first = names.stream().findFirst(); Optional<String> any = names.parallelStream().findAny();
6. 计数(Count)
count()
:返回元素数量。long count = names.stream().filter(name -> name.startsWith("C")).count(); // 1
7. 最大值和最小值
max(Comparator<T>)
和min(Comparator<T>)
:返回最大/最小值。Optional<String> longestName = names.stream().max(Comparator.comparingInt(String::length));
并行流(Parallel Stream)
Stream 支持并行处理,通过 parallelStream()
或 stream().parallel()
创建并行流:
long count = names.parallelStream() // 并行流.filter(name -> name.length() > 4).count();
- 注意:并行流适用于计算密集型且无状态的操作,否则可能导致线程安全问题。
示例:综合应用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");int sumOfLengths = names.stream().filter(name -> name.length() > 3) // 过滤长度大于3的名字.map(String::length) // 映射为长度.reduce(0, Integer::sum); // 求和System.out.println("Sum of lengths: " + sumOfLengths); // 输出:22
Stream 操作的特点
- 延迟执行:中间操作不会立即执行,只有在终止操作时才会触发。
- 一次性使用:流只能遍历一次,遍历后不能再次使用。
- 内部迭代:Stream 操作由库内部实现迭代,比外部迭代(如 for 循环)更简洁。
总结
Stream API 通过中间操作和终止操作的组合,提供了一种高效、灵活且易读的方式来处理集合数据。掌握这些操作可以让你的代码更简洁、更具表现力。