Java 8 引入的 Stream API 是一种 声明式 的数据处理方式,用于处理集合、数组等数据源。它将数据处理操作(如过滤、映射、排序、聚合)以链式方式组织,简化了集合的处理逻辑,提升了代码的可读性和可维护性。
一、Stream 的核心概念
1. 流(Stream)
- 流是一组元素的序列,支持顺序或并行处理。
- 流不是集合 ,它本身不存储数据,而是对数据源(如
List、Set、Map、数组等)进行操作。 - 流只能被消费一次,一旦执行终端操作,流就关闭,不能再使用。
2. 操作类型
Stream 的操作分为两类:
| 操作类型 | 说明 | 常见方法 |
|---|---|---|
| 中间操作(Intermediate Operations) | 返回一个新的流,可以链式调用 | filter()、map()、sorted()、distinct() |
| 终端操作(Terminal Operations) | 返回非流的结果(如 List、int、void) |
collect()、forEach()、count()、findFirst() |
二、Stream 的处理流程
java
List<String> result = list.stream()
.filter(s -> s.length() > 3)// 中间操作
.map(String::toUpperCase)// 中间操作
.sorted()// 中间操作
.collect(Collectors.toList());// 终端操作
流程图:
数据源(如 List) → 流 → 中间操作 → 中间操作 → ... → 终端操作 → 结果
三、常用操作与案例
1. 创建流(Stream)
java
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
// 从数组创建
String[] arr = {"x", "y", "z"};
Stream<String> stream2 = Arrays.stream(arr);
// 生成无限流
Stream<Integer> infiniteStream = Stream.iterate(1, n -> n + 1);
2. 中间操作
(1) filter(Predicate):过滤符合条件的元素
java
List<String> filtered = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
(2) map(Function):将元素映射为其他类型
java
List<Integer> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
(3) sorted(Comparator):排序
java
List<String> sorted = list.stream()
.sorted(Comparator.reverseOrder()) // 降序
.collect(Collectors.toList());
(4) distinct():去重
java
List<String> unique = list.stream()
.distinct()
.collect(Collectors.toList());
(5) limit(long) / skip(long):截取或跳过
java
List<String> limited = list.stream()
.limit(2) // 取前2个元素
.collect(Collectors.toList());
3. 终端操作
(1) collect(Collector):收集结果(最常用)
java
List<String> listResult = stream.collect(Collectors.toList());
Set<String> setResult = stream.collect(Collectors.toSet());
Map<String, Integer> mapResult = stream.collect(Collectors.toMap(...));
(2) forEach(Consumer):遍历
java
stream.forEach(System.out::println);
(3) count():统计元素数量
java
long count = stream.count();
(4) findFirst() / findAny():查找第一个或任意元素
java
Optional<String> first = stream.findFirst();
(5) anyMatch() / allMatch() / noneMatch():匹配判断
java
boolean hasLong = stream.anyMatch(s -> s.length() > 3);
(6) reduce():归约操作(如求和、最大值)
java
int sum = stream.mapToInt(Integer::intValue).sum();
Optional<Integer> max = stream.reduce(Integer::max);
四、高级用法与案例
1. 分组(Grouping By)
java
Map<String, List<User>> usersByGender = userList.stream()
.collect(Collectors.groupingBy(User::getGender));
2. 分区(Partitioning By)
java
Map<Boolean, List<User>> activeUsers = userList.stream()
.collect(Collectors.partitioningBy(User::isActive));
3. 统计(Summarizing)
java
IntSummaryStatistics stats = userList.stream()
.collect(Collectors.summarizingInt(User::getAge));
4. 多级分组
java
Map<String, Map<String, List<User>>> usersByGenderAndCity = userList.stream()
.collect(Collectors.groupingBy(User::getGender,
Collectors.groupingBy(User::getCity)));
5. 拼接字符串(Joining)
java
String names = userList.stream()
.map(User::getName)
.collect(Collectors.joining(", ")); // "Alice, Bob, Charlie"
五、并行流(Parallel Stream)
并行流利用多核 CPU 加速计算,适用于大数据量处理:
java
List<String> result = list.parallelStream()
.map(String::toUpperCase)
.collect(Collectors.toList());
⚠️ 注意:并行流不保证顺序,且对线程安全要求较高。
六、完整示例代码
java
import java.util.*;
import java.util.stream.Collectors;
class User {
private String name;
private int age;
private String gender;
public User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getGender() { return gender; }
@Override
public String toString() {
return name;
}
}
public class StreamDemo {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", 25, "Female"),
new User("Bob", 30, "Male"),
new User("Charlie", 22, "Male"),
new User("Diana", 28, "Female")
);
// 案例1:过滤 + 映射 + 收集
List<String> maleNames = users.stream()
.filter(u -> "Male".equals(u.getGender()))
.map(User::getName)
.collect(Collectors.toList());
System.out.println("Male names: " + maleNames);
// 案例2:分组 + 统计
Map<String, Double> avgAgeByGender = users.stream()
.collect(Collectors.groupingBy(User::getGender,
Collectors.averagingInt(User::getAge)));
System.out.println("Average age by gender: " + avgAgeByGender);
// 案例3:并行流求和
int totalAge = users.parallelStream()
.mapToInt(User::getAge)
.sum();
System.out.println("Total age: " + totalAge);
// 案例4:查找最年长用户
Optional<User> oldest = users.stream()
.max(Comparator.comparing(User::getAge));
System.out.println("Oldest user: " + oldest.orElse(null));
}
}
七、Stream 的优势
| 优势 | 说明 |
|---|---|
| 声明式语法 | 用链式调用表达逻辑,代码更简洁 |
| 函数式编程支持 | 使用 Lambda 表达式简化操作 |
| 链式操作 | 支持中间操作的灵活组合 |
| 并行处理 | 利用多核 CPU 提升性能 |
| 内置丰富的操作 | 提供过滤、映射、分组、统计等工具 |
八、注意事项
- 流只能消费一次:终端操作后,流关闭,再次使用会抛异常。
- 避免副作用 :中间操作(如
forEach)不应修改外部状态。 - 性能权衡:小数据集使用普通循环更高效,大数据集使用并行流。
- 线程安全:并行流操作需确保数据和操作是线程安全的。
九、总结
Java Stream 是一种 声明式 的数据处理工具,通过链式操作简化集合处理,提升了代码的可读性和可维护性。结合 Collectors、Optional、Comparator 等工具,可以轻松实现过滤、映射、分组、统计等复杂操作。合理使用 Stream 能显著提高开发效率和代码质量。