peek是stream的中间操作,用于调试时对每个元素执行消费动作但不改变流;需配合终端操作才生效,不适用于过滤或转换,并行流中输出顺序不确定。

peek 是 Java Stream API 中的一个中间操作,它允许你在不改变流元素的前提下对每个元素执行一个消费动作(比如打印日志),常用于调试。
peek 的基本用法和注意事项
peek 接收一个 Consumer<t></t>,对流中每个元素执行一次操作,但不会影响流本身。注意:它只在流真正执行终端操作时才触发,如果只有 peek 没有 forEach、collect 等终端操作,日志不会打印。
- 必须配合终端操作(如
forEach、count、collect)才会生效 - peek 不修改元素,也不能用来过滤或转换——那是
map或filter的职责 - 在并行流中,打印顺序不保证与原始顺序一致
常见调试场景示例
假设有一个整数列表,你想观察 filter 和 map 前后的数据变化:
List<integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.stream()
.peek(x -> System.out.println("原始: " + x))
.filter(x -> x % 2 == 0)
.peek(x -> System.out.println("过滤后: " + x))
.map(x -> x * 2)
.peek(x -> System.out.println("映射后: " + x))
.collect(Collectors.toList());</integer>
输出类似:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
原始: 2
过滤后: 2
映射后: 4
原始: 3
原始: 4
过滤后: 4
映射后: 8
...
替代方案与更安全的调试建议
peek 虽方便,但过度依赖可能让代码可读性下降。实际开发中可以考虑:
- 用 IDE 断点调试代替大量 peek 日志
- 对复杂逻辑,提取为独立方法并在方法入口/出口加日志
- 需要结构化日志时,用 SLF4J 等框架替代
System.out.println - 避免在生产环境保留 peek 日志(可通过条件编译或日志级别控制)
peek 在并行流中的行为
并行流中 peek 的执行线程不确定,日志可能交错输出。若需顺序日志,应改用串行流或改用终端操作中的日志(如 forEachOrdered)。
numbers.parallelStream()
.peek(x -> System.out.println(Thread.currentThread().getName() + ": " + x))
.filter(x -> x > 3)
.collect(Collectors.toList());
你会看到类似 ForkJoinPool.commonPool-worker-2: 5 这样的输出,说明日志来自不同线程。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










