peek是只观察不修改的中间操作,需配合终端操作生效,用于调试时实时查看流中元素值,支持链式调用但不可替代map/filter等转换逻辑。

直接用 peek() 在关键节点插入日志或断点,就能实时看到每个元素流经时的值——但它必须搭配终端操作才生效,且不能替代真正的转换逻辑。
peek 的定位:只看不改的“观察哨”
peek 是一个中间操作,本质是给流加监控探针。它接收一个 Consumer(比如 System.out::println),对每个流经的元素执行动作,但既不修改元素内容,也不影响后续流程。
- 它返回原流,支持链式调用
- 不会触发流执行,必须跟
collect()、forEach()等终端操作才能跑起来 - 在短路操作(如
findFirst())中,只作用于实际被处理的元素,不是全部
在每步操作后加 peek,看清数据怎么变
把 peek 放在 filter、map、sorted 等中间操作之后,就能确认该步是否按预期筛选或转换了数据。
- 比如过滤后加
.peek(s -> System.out.println("after filter: " + s)),能立刻知道哪些字符串过了关 - 映射后加
.peek(s -> System.out.println("after map: " + s)),可验证大小写、格式或字段提取是否正确 - 排序后加 peek,能检查顺序是否符合预期,尤其对自定义 Comparator 时很有用
避免常见误用:别把它当 map 或副作用入口
peek 不是设计来改数据的,强行在里面调 setter 或发 HTTP 请求,不仅违背语义,还可能因流惰性、并行执行或短路导致行为不可控。
立即学习“Java免费学习笔记(深入)”;
- 想改元素类型或内容,用
map();想筛数据,用filter() - 真要记录日志或存临时状态,确保操作是线程安全的,且不依赖执行顺序
- 生产环境慎用耗时操作(如写文件、远程调用),它会拖慢整个流处理
配合调试器快速定位问题
在 IDE 中,可以把 peek 的 lambda 表达式设为断点,运行时逐个停在每个元素上,查看变量值、调用栈和上下文。
- 比纯日志更灵活:可 inspect 对象内部字段、跳过某些元素、条件暂停
- 适合排查空指针、NPE、意外 null 值或字段未初始化等问题
- 注意:并行流中多个线程可能同时触发 peek,断点会频繁打断,建议先切回串行流调试


















