题目
使用Stream API优化集合操作并避免常见陷阱
信息
- 类型:问答
- 难度:⭐⭐
考点
Stream API原理,中间操作与终止操作,并行流使用注意事项
快速回答
核心要点:
- Stream操作分为中间操作(lazy)和终止操作(eager)
- 正确使用
collect(Collectors.toList())实现类型安全转换 - 并行流需满足无状态、无干扰、可结合条件
- 避免在Stream中修改外部状态
问题场景
给定员工列表,要求:
1. 过滤出薪资>8000的Java工程师
2. 提取姓名并转为大写
3. 收集到新List
初始代码(含缺陷)
List<Employee> employees = Arrays.asList(
new Employee("Alice", "Java", 9000),
new Employee("Bob", "Python", 7500),
new Employee("Charlie", "Java", 8500)
);
List<String> result = new ArrayList<>();
employees.parallelStream()
.filter(e -> {
if ("Java".equals(e.getDept()) && e.getSalary() > 8000) {
result.add(e.getName().toUpperCase()); // 错误1:修改外部状态
return true;
}
return false;
})
.count(); // 错误2:误用终止操作问题分析
- 错误1:副作用操作 - 在filter中修改外部result集合,破坏函数式编程原则
- 错误2:终止操作误用 - count()会触发计算但丢弃过滤结果
- 并行风险 - 直接操作非线程安全的ArrayList导致数据竞争
正确实现
List<String> result = employees.stream()
.filter(e -> "Java".equals(e.getDept()) && e.getSalary() > 8000)
.map(e -> e.getName().toUpperCase())
.collect(Collectors.toList()); // 正确终止操作最佳实践
- 无状态操作:避免在lambda中修改外部变量
- 操作分离:filter只负责过滤,map负责转换
- 正确终止:使用collect/toList等明确收集结果
- 并行流谨慎使用:
- 数据量>10万时考虑
- 确保操作无状态:
map优于forEach - 避免共享可变状态
并行流优化示例
List<String> result = employees.parallelStream()
.filter(e -> e.getDept().equals("Java") && e.getSalary() > 8000)
.map(Employee::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());常见错误
| 错误类型 | 示例 | 后果 |
|---|---|---|
| 忽略终止操作 | stream.filter(...).map(...) | 实际不执行 |
| 重复使用Stream | Stream s=...; s.count(); s.collect() | IllegalStateException |
| 修改源数据 | stream.peek(list::remove) | ConcurrentModificationException |
原理说明

Stream处理分为三个阶段:
1. 创建:集合/数组创建Stream
2. 中间操作:filter/map等延迟操作,生成新Stream
3. 终止操作:触发实际计算,返回结果
扩展知识
- 性能监控:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=4调整并行度 - 短路操作:findFirst/anyMatch可提前终止
- 收集器进阶:
Collectors.groupingBy实现分组统计