Java Stream 流式编程
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 的处理流程
List<String> result = list.stream()
.filter(s -> s.length() > 3)// 中间操作
.map(String::toUpperCase)// 中间操作
.sorted()// 中间操作
.collect(Collectors.toList());// 终端操作
流程图:
数据源(如 List) → 流 → 中间操作 → 中间操作 → ... → 终端操作 → 结果
三、常用操作与案例
1. 创建流(Stream)
// 从集合创建
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):过滤符合条件的元素
List<String> filtered = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
(2) map(Function):将元素映射为其他类型
List<Integer> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
(3) sorted(Comparator):排序
List<String> sorted = list.stream()
.sorted(Comparator.reverseOrder()) // 降序
.collect(Collectors.toList());
(4) distinct():去重
List<String> unique = list.stream()
.distinct()
.collect(Collectors.toList());
(5) limit(long) / skip(long):截取或跳过
List<String> limited = list.stream()
.limit(2) // 取前2个元素
.collect(Collectors.toList());
3. 终端操作
(1) collect(Collector):收集结果(最常用)
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):遍历
stream.forEach(System.out::println);
(3) count():统计元素数量
long count = stream.count();
(4) findFirst() / findAny():查找第一个或任意元素
Optional<String> first = stream.findFirst();
(5) anyMatch() / allMatch() / noneMatch():匹配判断
boolean hasLong = stream.anyMatch(s -> s.length() > 3);
(6) reduce():归约操作(如求和、最大值)
int sum = stream.mapToInt(Integer::intValue).sum();
Optional<Integer> max = stream.reduce(Integer::max);
四、高级用法与案例
1. 分组(Grouping By)
Map<String, List<User>> usersByGender = userList.stream()
.collect(Collectors.groupingBy(User::getGender));
2. 分区(Partitioning By)
Map<Boolean, List<User>> activeUsers = userList.stream()
.collect(Collectors.partitioningBy(User::isActive));
3. 统计(Summarizing)
IntSummaryStatistics stats = userList.stream()
.collect(Collectors.summarizingInt(User::getAge));
4. 多级分组
Map<String, Map<String, List<User>>> usersByGenderAndCity = userList.stream()
.collect(Collectors.groupingBy(User::getGender,
Collectors.groupingBy(User::getCity)));
5. 拼接字符串(Joining)
String names = userList.stream()
.map(User::getName)
.collect(Collectors.joining(", ")); // "Alice, Bob, Charlie"
五、并行流(Parallel Stream)
并行流利用多核 CPU 加速计算,适用于大数据量处理:
List<String> result = list.parallelStream()
.map(String::toUpperCase)
.collect(Collectors.toList());
⚠️ 注意:并行流不保证顺序,且对线程安全要求较高。
六、完整示例代码
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 能显著提高开发效率和代码质量。
