Java Stream
Java 8 声明式数据处理管道:中间操作惰性求值,终止操作触发执行;不修改源集合。详解 Stream、Lambda与函数式编程。
Stream 是什么
| 特点 | 说明 |
|---|---|
| 不存数据 | 元素来自集合/数组/生成器 |
| 不修改源 | 产生新结果 |
| 惰性中间操作 | filter/map 链在 collect 等终止操作前不执行 |
| 可并行 | parallelStream() / parallel() |
操作分类
source → 中间操作* → 终止操作(必须)
filter/map/flatMap/sorted/distinct/limit
collect/forEach/reduce/count/findFirst
一次 Stream 只能消费一次;终止后再用需重新 stream()。
常用 API
| 类型 | 示例 |
|---|---|
| 创建 | list.stream()、Stream.of()、IntStream.range(1,5) |
| 过滤 | filter、distinct、limit、skip |
| 转换 | map、flatMap、mapToInt |
| 排序 | sorted()、sorted(Comparator.comparing(...)) |
| 收集 | collect(toList())、groupingBy、toMap、joining |
| 规约 | reduce、count、sum(数值流) |
| 匹配 | anyMatch、allMatch、findFirst |
List<String> names = users.stream()
.filter(u -> u.getAge() >= 18)
.map(User::getName)
.sorted()
.collect(Collectors.toList());map vs flatMap
| map | flatMap | |
|---|---|---|
| 关系 | 1 → 1 | 1 → 0..n,再展平 |
| 例 | map(String::length) | flatMap(s -> Arrays.stream(s.split(" "))) |
collect 常考
// 分组
Map<String, List<User>> byDept =
users.stream().collect(Collectors.groupingBy(User::getDept));
// toMap(注意 key 冲突)
Map<Long, User> map = users.stream()
.collect(Collectors.toMap(User::getId, u -> u, (a, b) -> b));
// 字符串拼接
Collectors.joining(", ", "[", "]");toMap 无 merge 函数时 key 重复抛 IllegalStateException。
Stream vs 传统 for
| for | Stream | |
|---|---|---|
| 风格 | 命令式 | 声明式 |
| 并行 | 手动 | parallelStream |
| 调试 | 易 | 链长时难 |
| 性能 | 小数据 often 更快 | 大数据 + 并行有优势 |
简单遍历、需 break/continue 时 for 更直观;过滤-映射-聚合 用 Stream 更清晰。
并行流注意
- 使用
ForkJoinPool.commonPool() - 数据量小时并行更慢(调度开销)
forEach无序;有序用forEachOrdered- 禁止在 pipeline 里改共享变量;用
collect等线程安全归约
常见面试题
Q:Stream 和 Collection 区别?
A:Collection 是数据结构;Stream 是计算视图,一次性管道。
Q:中间操作为什么叫惰性?
A:只有遇到 collect/forEach 等终止操作才遍历;可短路(findFirst + filter)。
Q:peek 能改元素吗?
A:可以改对象内部字段(引用类型);不要依赖 peek 做业务逻辑,仅调试。
Q:reduce 和 collect?
A:reduce 归约为单个值(Optional);collect 用 Collector 转为 List/Map 等可变容器。
Q:IntStream 好处?
A:避免 Integer 装箱,提供 sum/average/summaryStatistics。