Java 8 引入的 Stream API 是一种强大的处理集合数据的工具,它允许你以声明式方式处理数据集合(如 List、Set 等),并支持多种聚合操作(如过滤、映射、排序、归约等)。Stream API 可以显著提高代码的可读性和简洁性,同时支持并行处理以提升性能。
一、Stream 基础概念
1. 什么是 Stream?
- Stream 是一种针对集合数据的高级抽象,它不存储数据,而是定义了一系列对数据源(如集合、数组)的操作。
- Stream 操作是惰性的,只有在终止操作被调用时才会执行。
- Stream 可以是串行的或并行的,并行流利用多线程提高处理大数据的效率。
2. Stream 的特点
- 不改变数据源:Stream 操作不会修改原始数据,而是返回一个新的 Stream。
- 一次性使用:Stream 只能被消费一次,消费后需要重新创建。
- 延迟执行:中间操作(如 filter、map)只会记录操作,不会立即执行,直到终止操作(如 collect、forEach)被调用。
二、Stream 的创建
1. 从集合创建
java
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream = list.stream(); // 串行流
Stream<String> parallelStream = list.parallelStream(); // 并行流
2. 从数组创建
java
int[] array = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(array);
3. 使用 Stream.of ()
java
Stream<String> stream = Stream.of("apple", "banana", "cherry");
4. 创建无限流
java
// 生成无限序列(0, 2, 4, 6...)
Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);
// 生成随机数流
Stream<Double> randoms = Stream.generate(Math::random);
三、Stream 的中间操作
中间操作返回一个新的 Stream,允许链式调用多个操作。常见的中间操作包括:
1. 过滤元素:filter()
java
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
List<String> filtered = fruits.stream()
.filter(fruit -> fruit.startsWith("a")) // 保留以 'a' 开头的元素
.collect(Collectors.toList()); // [apple]
2. 映射元素:map()
java
List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> squared = numbers.stream()
.map(n -> n * n) // 平方每个元素
.collect(Collectors.toList()); // [1, 4, 9]
3. 扁平化嵌套结构:flatMap()
java
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4)
);
List<Integer> flattened = nested.stream()
.flatMap(List::stream) // 将嵌套列表展开为单个流
.collect(Collectors.toList()); // [1, 2, 3, 4]
4. 排序:sorted()
java
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
List<String> sorted = names.stream()
.sorted() // 自然排序
.collect(Collectors.toList()); // [Alice, Bob, Charlie]
5. 去重:distinct()
java
List<Integer> duplicates = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> unique = duplicates.stream()
.distinct() // 去重
.collect(Collectors.toList()); // [1, 2, 3]
6. 截断:limit()
和 skip()
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limited = numbers.stream()
.limit(3) // 取前3个元素
.collect(Collectors.toList()); // [1, 2, 3]
List<Integer> skipped = numbers.stream()
.skip(2) // 跳过前2个元素
.collect(Collectors.toList()); // [3, 4, 5]
四、Stream 的终止操作
终止操作触发 Stream 的执行并返回最终结果。常见的终止操作包括:
1. 收集结果:collect()
java
// 收集到 List
List<String> resultList = stream.collect(Collectors.toList());
// 收集到 Set
Set<String> resultSet = stream.collect(Collectors.toSet());
// 收集到 Map
Map<Integer, String> map = stream
.collect(Collectors.toMap(
s -> s.length(), // key: 字符串长度
s -> s, // value: 字符串本身
(existing, replacement) -> existing // 处理键冲突的策略
));
2. 遍历元素:forEach()
java
stream.forEach(System.out::println); // 打印每个元素
3. 聚合操作:reduce()
java
// 求和
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b); // 初始值为0,累加所有元素
// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max); // 返回 Optional 避免空指针
4. 匹配元素:anyMatch()
、allMatch()
、noneMatch()
java
boolean anyStartsWithA = stream.anyMatch(s -> s.startsWith("A")); // 是否存在以 'A' 开头的元素
boolean allStartsWithA = stream.allMatch(s -> s.startsWith("A")); // 是否所有元素都以 'A' 开头
boolean noneStartsWithA = stream.noneMatch(s -> s.startsWith("A")); // 是否没有元素以 'A' 开头
5. 查找元素:findFirst()
、findAny()
java
Optional<String> first = stream.findFirst(); // 返回第一个元素(可能为空)
Optional<String> any = stream.findAny(); // 返回任意元素(并行流中可能随机返回)
6. 统计:count()
、min()
、max()
java
long count = stream.count(); // 元素个数
Optional<Integer> min = numbers.stream().min(Integer::compareTo); // 最小值
Optional<Integer> max = numbers.stream().max(Integer::compareTo); // 最大值
五、并行流(Parallel Stream)
并行流利用多核处理器并行执行 Stream 操作,适用于大数据集的高效处理。
1. 创建并行流
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> parallelStream = numbers.parallelStream(); // 直接创建并行流
Stream<Integer> parallelStream2 = numbers.stream().parallel(); // 将串行流转换为并行流
2. 并行流示例
java
long sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum(); // 并行求和
3. 注意事项
- 线程安全:并行流操作涉及多线程,确保操作的数据源和中间操作是线程安全的。
- 适用场景:对于小数据集或操作本身开销不大的情况,并行流可能比串行流更慢(线程调度开销)。
- 顺序问题:并行流不保证处理顺序,若需要顺序性,可使用
forEachOrdered()
替代forEach()
。
六、Collectors 工具类
Collectors
是一个实用工具类,提供了各种收集器(Collector),用于将 Stream 结果收集到不同的数据结构中。
1. 分组:groupingBy()
java
// 按字符串长度分组
Map<Integer, List<String>> groups = stream
.collect(Collectors.groupingBy(String::length));
2. 分区:partitioningBy()
java
// 按条件分区(偶数和奇数)
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
3. 连接字符串:joining()
java
String joined = stream
.collect(Collectors.joining(", ")); // 用逗号连接元素
4. 统计:summarizingInt()
java
IntSummaryStatistics stats = numbers.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
// stats 包含 count, sum, min, max, average
七、Stream 与 Lambda 表达式结合
Stream API 与 Lambda 表达式紧密结合,使代码更简洁。例如:
java
// 传统方式
List<String> filtered = new ArrayList<>();
for (String fruit : fruits) {
if (fruit.startsWith("a")) {
filtered.add(fruit);
}
}
// Stream + Lambda 方式
List<String> filtered = fruits.stream()
.filter(fruit -> fruit.startsWith("a"))
.collect(Collectors.toList());
八、常见使用场景
1. 数据过滤与转换
java
List<Product> products = getProducts();
List<String> productNames = products.stream()
.filter(p -> p.getPrice() > 100) // 过滤价格大于100的产品
.map(Product::getName) // 提取产品名称
.collect(Collectors.toList());
2. 聚合统计
java
double averagePrice = products.stream()
.mapToDouble(Product::getPrice)
.average() // 计算平均价格
.orElse(0.0);
3. 并行处理
java
// 并行计算所有产品的总价格
double totalPrice = products.parallelStream()
.mapToDouble(Product::getPrice)
.sum();
总结
Java Stream API 通过声明式方式处理集合数据,使代码更简洁、易读,并支持并行处理以提高性能。关键要点:
- 中间操作(如
filter
、map
)返回新的 Stream,支持链式调用。 - 终止操作(如
collect
、forEach
)触发执行并产生结果。 - 并行流适用于大数据集的高效处理,但需注意线程安全和适用场景。
- Collectors 工具类提供丰富的收集器,用于结果转换和聚合。