
Consumer 为什么不能直接打印 List
类型不匹配是第一个拦路虎。Consumer 接口本身不操作集合,它只消费单个元素;传 List 给 Consumer 不会自动遍历,更不会隐式拆箱。常见错误是写成 list.forEach(System.out::println) 却发现编译失败——其实是 list 类型不对(比如是 Object 数组或泛型擦除后失配),或者误以为 Consumer 能接管整个容器。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 确认集合元素类型和
Consumer泛型参数一致,比如Consumer<string></string>只能用于List<string></string> - 不要试图把
Consumer<list>></list>当作“打印整个列表”的捷径,它接收的是整个对象,不是每个元素 - 若需自定义打印逻辑(如加前缀),用 lambda 更清晰:
list.forEach(s -> System.out.println("[LOG] " + s))
forEach 遇到 NullPointerException 怎么办
NullPointerException 多半来自集合本身为 null,而非元素为空。Java 的 forEach 不做空检查,调用 null.forEach(...) 直接抛异常,这和传统 for 循环不同,容易忽略。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 在调用前加空判断:
if (list != null) list.forEach(...) - 用
Objects.requireNonNull(list)显式暴露问题,比静默 NPE 更利于调试 - 如果集合来自外部 API 或 Optional,优先用
optional.orElse(Collections.emptyList()).forEach(...)
Consumer 和 for-each 循环性能差多少
几乎没差别。底层 forEach 就是用增强 for 循环实现的(Iterable.forEach 默认委托给 iterator()),函数式写法只是语法糖,不引入额外对象分配或反射开销。但要注意:每次传入的 lambda 如果捕获局部变量,可能触发匿名内部类生成(Java 8)或合成方法(Java 11+),不过对打印这种简单操作影响可忽略。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 别为了“看起来更函数式”而嵌套 Consumer,比如
list.forEach(s -> consumer.accept(s))——直接用list.forEach(consumer) - 避免在 lambda 中做耗时操作(如 IO、同步锁),否则阻塞主线程且难以追踪
- 若需中断遍历(如找到第一个匹配就停),
forEach不支持 break,老实用传统循环
Stream.peek 和 forEach 打印的区别在哪
peek 是中间操作,必须跟终端操作(如 count()、collect())才能触发;单独写 stream.peek(System.out::println) 不会输出任何东西。而 forEach 是终端操作,一调就执行。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 调试流处理过程用
peek,比如list.stream().filter(...).peek(System.out::println).map(...).collect(...) - 单纯打印结果用
forEach,语义明确、无副作用陷阱 -
peek在并行流中输出顺序不确定,forEach在并行流里也不保证顺序;要顺序输出,用forEachOrdered
Consumer 看似简单,但类型擦除、null 安全、流生命周期这些点一旦混在一起,很容易打出预期外的结果。最常被忽略的是:它不解决“怎么拿到数据”,只解决“拿到之后怎么用”。











