Java 8 最重要的 API:Stream 全面解析与实战指南
在 Java 8 中,Stream(流) 是最具革命性的 API 之一,它将 Java 编程从“命令式”风格,带入了更简洁、更高效的“函数式”风格。
本文将从基础概念、核心 API、典型案例到最佳实践,系统梳理 Stream 的使用方式。
一、什么是 Stream(流)?
Stream 本质上是对数据集合的一种抽象视图,它不是数据本身,而是对数据进行声明式处理的流水线。
核心特点:
- 不存储数据(数据来源于集合、数组等)
- 支持函数式操作(filter、map 等)
- 惰性执行(中间操作不会立即执行)
- 一次性消费(一个流只能使用一次)
二、从命令式到函数式:对比案例
1. Java 8 之前写法(命令式)
需求:筛选出姓张的用户 → 按年龄排序 → 获取姓名列表
List<User> users = getUsers();
List<User> zhangUsers = new ArrayList<>();
for (User user : users) {
if (user.name.startsWith("张")) {
zhangUsers.add(user);
}
}
Collections.sort(zhangUsers, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.age - o2.age;
}
});
List<String> names = new ArrayList<>();
for (User user : zhangUsers) {
names.add(user.name);
}
问题:
- 代码冗长
- 关注“怎么做”(过程),而不是“做什么”(意图)
- 易出 bug
2. Stream 写法(函数式)
users.stream()
.filter(User::isSurnameZhang)
.sorted(Comparator.comparing(User::getAge))
.map(User::getName)
.collect(Collectors.toList());
优势:
- 一行表达完整逻辑
- 可读性强(接近自然语言)
- 更易维护与组合
三、Stream 的创建方式
常见创建方式:
Collection.stream() // 集合
Stream.of(...) // 显式创建
String.chars() // 字符流
IntStream.range() // 数值范围
示例:
IntStream.range(1, 10)
四、Stream 的操作分类
Stream 操作分为两类:
1. 中间操作(返回 Stream)
- filter(过滤)
- map(映射)
- sorted(排序)
- flatMap(扁平化)
特点:惰性执行
2. 终结操作(返回非 Stream)
- forEach
- count / max / min
- findFirst / findAny
- anyMatch / noneMatch
- collect
特点:触发执行
⚠️ 注意:一个流只能被消费一次
五、核心操作详解
1. filter(过滤)
.filter(user -> user.getAge() >= 60)
2. map(映射)
将一个对象转换为另一个对象:
.map(User::getName)
3. sorted(排序)
.sorted(Comparator.comparing(User::getAge))
4. flatMap(扁平化)
list.stream()
.map(s -> s.split(" "))
.flatMap(Stream::of)
.collect(Collectors.toList());
5. anyMatch(短路操作)
boolean result = users.stream()
.anyMatch(user -> user.getName().startsWith("张"));
6. collect(最强大的终结操作)
.collect(Collectors.toList());
常见用法:
- toList()
- toSet()
- toCollection()
示例(使用 TreeSet):
TreeSet<String> result = users.stream()
.map(User::getName)
.collect(Collectors.toCollection(TreeSet::new));
7. groupingBy(分组)
Map<String, List<User>> result =
users.stream()
.collect(Collectors.groupingBy(User::getDepartment));
8. joining(字符串拼接)
String result = words.stream()
.filter(w -> w.length() == 1)
.collect(Collectors.joining(","));
六、Optional 的正确使用方式
推荐用法(链式调用)
User user = users.stream()
.filter(User::isSurnameZhang)
.findAny()
.orElseThrow(IllegalAccessError::new);
❌ 错误用法(当作 null)
Optional<User> optionalUser = users.stream().findAny();
if (optionalUser.isPresent()) {
optionalUser.get();
}
核心原则:
- Optional 用于返回值表达“可能为空”
- 不用于替代 if-null 逻辑
七、实用案例
1. 统计大写字母数量
public static int countUpperCaseLetters(String str) {
return (int) str.chars()
.filter(Character::isUpperCase)
.count();
}
2. 条件筛选 + 排序 + 收集
public static LinkedList<String> collectNames(List<User> users) {
return users.stream()
.filter(user -> user.getAge() >= 60)
.sorted(Comparator.comparing(User::getAge).reversed())
.map(User::getName)
.collect(Collectors.toCollection(LinkedList::new));
}
3. 分组 + 排序
employees.stream()
.sorted(Comparator.comparing(Employee::getAge))
.collect(Collectors.groupingBy(Employee::getDepartment));
4. 转 Map
Map<Integer, Order> map =
orders.stream()
.collect(Collectors.toMap(Order::getId, item -> item));
八、并行流(parallelStream)
示例:
IntStream.range(1, 1_000_000)
.parallel()
.filter(Primes::isPrime)
.count();
特点:
- 利用多核 CPU 并行计算
- 理论上接近线性性能提升
⚠️ 注意事项:
- 仅适用于无状态、无副作用操作
- 必须进行性能测试
- 默认并行度 ≈ CPU 核数 - 1
👉 结论:不确定就不要用
九、调试技巧
推荐使用插件:
Java Stream Debugger
可以可视化每一步 Stream 的数据变化过程。
十、总结
Stream 的本质,是一种声明式数据处理模型:
- 用“做什么”替代“怎么做”
- 用函数式思想提升表达力
- 用链式调用提高可读性
核心心法:
过滤(filter)→ 转换(map)→ 排序(sorted)→ 收集(collect)
掌握这一条主线,你已经掌握了 Stream 的 80% 用法。