804 字
4 分钟
Java Lambda 与 Stream:常用操作、惰性求值与并行边界

Lambda 和 Stream 是常用业务代码知识。面试重点是能读懂筛选、转换、分组代码,并解释执行时机;不需要钻进流管道源码。

Lambda 与函数式接口#

函数式接口只有一个抽象方法,Lambda 可以提供这个方法的实现。常见接口包括 Predicate、Function、Consumer、Supplier;default 和 static 方法不计入抽象方法数量。

Predicate<String> nonEmpty = text -> !text.isEmpty();
Function<String, Integer> length = String::length;

方法引用是 Lambda 的简洁表达。Lambda 捕获的局部变量必须是 final 或事实上不再重新赋值的变量;被引用对象的内部状态仍可能改变。

Stream 与集合有什么区别?#

集合保存数据,Stream 描述数据处理管道,通常不保存另一份完整数据。管道由数据源、中间操作、终止操作构成。

List<String> result = names.stream()
.filter(name -> name != null)
.map(String::trim)
.filter(name -> !name.isEmpty())
.distinct()
.collect(Collectors.toList());

这里 filter 筛选、map 转换、distinct 去重,collect 收集结果。distinct 对对象的去重依赖 equals/hashCode,错误的相等定义也会影响流处理。

常见操作#

操作作用
filter保留满足条件的元素
map一个元素转换为一个结果
flatMap将多个子流展开成一个流
sorted按自然顺序或 Comparator 排序
collect收集为列表、Map 或分组结果
reduce按规则归约,例如求和
anyMatch / findFirst判断或查找,可以短路

Collectors.toMap 的 key 重复时,未提供合并规则通常会抛异常;需要明确冲突如何处理。groupingBy 则适合把同一个 key 对应的多个元素分组。

什么是惰性求值?#

filter、map 等中间操作通常不会立即遍历数据,终止操作触发执行。有些终止操作会短路,处理到满足条件即可结束,因此不要把必需的业务副作用藏在 peek 等中间操作中。

Stream 一般只能消费一次,终止操作后不能再重用同一条流。需要再次处理时,从原集合创建新的流。

parallelStream 一定更快吗?#

不一定。并行拆分、调度和合并结果有成本;数据量小、任务简单时可能更慢。并行处理还要求操作适合拆分,避免修改共享变量。

parallelStream 常使用公共 ForkJoinPool,不适合毫无隔离地执行大量阻塞网络调用。若需要明确的超时、线程数和下游限流,应使用专门的异步与线程池方案。

面试回答模板#

Lambda 用于实现函数式接口,Stream 用声明式管道处理数据。filter、map 等是中间操作,通常惰性执行,collect、count 等终止操作触发计算。Stream 不等于集合,也不能重复消费。并行流需要考虑数据规模、共享状态和公共线程池竞争,不能直接认为并行一定更快。

自测#

  1. map 与 flatMap 有什么区别?前者逐元素转换,后者展开子流。
  2. 为什么 collect 后再次操作同一条 Stream 会失败?流已经被消费。
  3. 并行流里向同一个 ArrayList 添加元素安全吗?普通 ArrayList 不提供并发写入保证,应使用正确的收集方式。
Java Lambda 与 Stream:常用操作、惰性求值与并行边界
https://blog.xqcherry.top/posts/interview/java/java-lambda-stream/
作者
xqcherry
发布于
2026-10-12
许可协议
CC BY-NC-SA 4.0