Java Stream 流详解

Java 8 引入了 Stream API,它是一种 声明式 处理数据的方式,旨在简化集合类的操作。Stream 允许你以声明性方式处理集合、数组、I/O 通道等的数据流,支持并行处理,并能简洁地执行过滤、映射、排序等操作。


1. 什么是 Stream

Stream 是一种 数据流,不是数据存储结构。它允许你以一种 函数式 的方式处理集合中的数据,通过流水线方式进行操作,支持 惰性计算(即只有在最终操作时才会执行)。Stream 操作通常分为两大类:

  • 中间操作(Intermediate Operations):返回一个新的流,支持链式调用,操作是惰性求值的。
  • 终止操作(Terminal Operations):触发流的计算,并产生一个结果(如聚合、输出等),一旦执行终止操作,流就不可再被使用。

2. Stream 的常见创建方式

Stream 可以通过以下几种方式创建:

2.1 从集合中创建 Stream

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry");

// 通过集合创建流
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
}
}

2.2 从数组中创建 Stream

1
2
3
String[] arr = {"apple", "banana", "cherry"};
Stream<String> stream = Arrays.stream(arr);
stream.forEach(System.out::println);

2.3 创建无限流

使用 Streamgenerateiterate 方法可以创建无限流。

1
2
3
4
5
6
7
// 使用 generate 创建无限流
Stream<Double> randomNumbers = Stream.generate(Math::random);
randomNumbers.limit(5).forEach(System.out::println);

// 使用 iterate 创建无限流
Stream<Integer> numbers = Stream.iterate(0, n -> n + 2);
numbers.limit(5).forEach(System.out::println); // 0, 2, 4, 6, 8

3. Stream 中的常见中间操作

中间操作是惰性计算的,即它们只会在终止操作触发时被执行。常见的中间操作包括:

3.1 filter()

根据条件过滤流中的元素。

1
2
3
4
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
list.stream()
.filter(s -> s.startsWith("a"))
.forEach(System.out::println); // 输出:apple

3.2 map()

对流中的每个元素应用转换函数。

1
2
3
4
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // 输出:APPLE BANANA CHERRY

3.3 distinct()

去重,返回一个没有重复元素的新流。

1
2
3
4
List<Integer> list = Arrays.asList(1, 2, 3, 3, 4, 5, 5);
list.stream()
.distinct()
.forEach(System.out::println); // 输出:1 2 3 4 5

3.4 sorted()

排序,默认按自然顺序排序,或者传入自定义的比较器。

1
2
3
4
List<Integer> list = Arrays.asList(3, 1, 4, 1, 5);
list.stream()
.sorted()
.forEach(System.out::println); // 输出:1 1 3 4 5

3.5 limit()

限制流的元素数量。

1
2
3
4
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream()
.limit(3)
.forEach(System.out::println); // 输出:1 2 3

3.6 skip()

跳过流中的前 N 个元素。

1
2
3
4
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream()
.skip(2)
.forEach(System.out::println); // 输出:3 4 5

3.7 flatMap()

扁平化流,将一个流中的元素映射为多个元素,并将这些元素扁平化成一个流。

1
2
3
4
5
List<List<String>> list = Arrays.asList(
Arrays.asList("a", "b"), Arrays.asList("c", "d"));
list.stream()
.flatMap(List::stream)
.forEach(System.out::println); // 输出:a b c d

4. Stream 中的常见终止操作

终止操作会触发整个流的计算,且终止操作执行后,流将无法再使用。

4.1 forEach()

对流中的每个元素执行指定的操作。

1
2
3
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.stream()
.forEach(System.out::println); // 输出:apple banana cherry

4.2 collect()

将流中的元素收集到集合中。常用的收集器有 Collectors.toList()Collectors.toSet()Collectors.joining() 等。

1
2
3
4
5
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> result = list.stream()
.filter(s -> s.startsWith("a"))
.collect(Collectors.toList());
System.out.println(result); // 输出:[apple]

4.3 reduce()

将流中的元素反复结合起来,得到一个值。通常用于计算总和、乘积等。

1
2
3
4
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = list.stream()
.reduce(0, Integer::sum);
System.out.println(sum); // 输出:10

4.4 anyMatch()

判断流中是否有任何一个元素满足给定条件。

1
2
3
4
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean result = list.stream()
.anyMatch(s -> s.startsWith("b"));
System.out.println(result); // 输出:true

4.5 allMatch()

判断流中是否所有元素都满足给定条件。

1
2
3
4
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean result = list.stream()
.allMatch(s -> s.length() > 4);
System.out.println(result); // 输出:true

4.6 noneMatch()

判断流中是否没有任何元素满足给定条件。

1
2
3
4
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean result = list.stream()
.noneMatch(s -> s.startsWith("z"));
System.out.println(result); // 输出:true

5. 并行流

Stream 还支持并行处理数据,通过 parallelStream()parallel() 方法,可以实现多核处理,提升性能(尤其是在处理大量数据时)。

1
2
3
4
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream()
.parallel()
.forEach(System.out::println); // 使用并行流输出

注意:并行流并不总是适合所有情况,对于小数据集,可能反而会导致性能下降。


6. 总结

  • 中间操作:如 filter()map()sorted() 等,是惰性求值的,返回一个新的流。
  • 终止操作:如 forEach()collect()reduce() 等,触发流的计算并产生结果。
  • 并行流Stream 支持并行处理,适合大数据量的处理。
  • 流的特点:流是一次性的,一旦消费后就无法重用。

通过 Stream API,我们可以更加简洁地处理集合数据,同时支持函数式编程风格。