foreach是终止操作,执行后流结束且返回void,不可链式续写;peek是中间操作,仅用于调试观察,必须配合终止操作才生效,二者不可混用。

ForEach 是终止操作,执行后流就结束了;Peek 是中间操作,只用于观察,必须配合终止操作才生效。用错位置或忽略惰性求值,代码看似写了却什么也不输出。
ForEach:适合最终处理,不能链式续写
它消耗整个流,执行副作用(如打印、入库、发消息),返回 void,之后不能再调用 map/filter 等操作。
- 直接遍历并修改对象属性可以,但注意线程安全和可读性
- 若只需简单遍历集合,优先用 list.forEach()(Iterable 接口方法),比 stream().forEach() 更轻量
- 并行流中 forEach 不保证顺序,需顺序执行时改用 forEachOrdered
Peek:专为调试设计,不是“带返回值的 forEach”
它不改变元素,也不收集结果,只是在元素流经某步时“看一眼”。没有终止操作,peek 一概不执行。
- 典型用法:在 filter、map 后插入 .peek(System.out::println) 查看中间结果
- 处理对象时建议输出关键字段,比如 .peek(o -> System.out.println("id=" + o.getId() + ", name=" + o.getName()))
- 严禁在 peek 里修改外部变量、添加到集合、或依赖执行顺序——这些行为不可靠,尤其在并行流中
常见失效场景与避坑要点
写完 peek 却没输出?大概率是漏了终止操作,或者用了无效果的中间操作收尾。
- 没加 collect/forEach/count 等终止操作 → peek 完全不触发
- 只调用 stream().peek(...) 然后结束方法 → 编译通过,但控制台静悄悄
- 在并行流中依赖 peek 输出顺序 → 实际顺序不确定,调试时建议先用串行流
- 用 peek 替代 collect 收集数据 → 正确做法是用 collect(Collectors.toList()),而非往外部 list.add()
怎么选:看目标,而不是看“能不能写”
想确认某步过滤是否按预期留下元素?用 peek。想把结果写进数据库?用 forEach 或 collect。想快速遍历改属性?用集合自身的 forEach。
- 调试链式流程 → peek + 终止操作(如 collect)
- 单次消费、无需返回 → forEach(或 Iterable.forEach)
- 需要新集合或聚合结果 → collect / reduce / toArray
- 临时记录日志 → peek 可接 SLF4J logger,但上线前建议移除或降级为 trace 级
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











