面试知识库
基础

Lambda与Stream#

一句话答案#

Lambda 是匿名函数简写(函数式接口的实例),Stream 提供声明式集合操作(filter/map/reduce),支持惰性求值和并行处理。

核心要点

Stream 的三段式结构:

数据源 → 中间操作(惰性,不立即执行)→ 终端操作(触发实际计算)
plaintext

常用中间操作(返回 Stream,惰性求值):

stream.filter(x -> x > 10)           // 过滤
      .map(x -> x * 2)               // 映射转换
      .flatMap(list -> list.stream()) // 扁平化(一对多映射)
      .distinct()                     // 去重
      .sorted()                       // 排序
      .peek(System.out::println)      // 调试窥探(不影响数据)
      .limit(10)                      // 取前 N 个
      .skip(5)                        // 跳过前 N 个
java

常用终端操作(触发计算,返回结果):

stream.forEach(System.out::println)           // 遍历
stream.collect(Collectors.toList())           // 收集为 List
stream.collect(Collectors.groupingBy(x::getType))  // 分组
stream.reduce(0, Integer::sum)                // 归约
stream.count()                                // 计数
stream.anyMatch(x -> x > 10)                  // 是否存在匹配
stream.findFirst()                            // 取第一个(返回 Optional)
stream.toArray()                              // 转数组
java

惰性求值(Lazy Evaluation):

// 中间操作不会立即执行,只有终端操作触发时才真正计算
List<String> result = names.stream()
    .filter(name -> {
        System.out.println("filter: " + name);  // 如果没有终端操作,这行永远不会打印
        return name.length() > 3;
    })
    .map(String::toUpperCase)
    .collect(Collectors.toList());  // 终端操作触发整个管道执行

// 优势:短路操作(如 findFirst)可以提前终止,不会遍历所有元素
Optional<String> first = names.stream()
    .filter(name -> name.startsWith("A"))
    .findFirst();  // 找到第一个就停止,不会继续 filter 后面的元素
java

Stream 的常见面试编码题:

parallelStream 的注意事项:

① 底层使用 ForkJoinPool.commonPool()(默认线程数 = CPU 核数 - 1)
② 不适合 IO 密集型操作(阻塞公共线程池,影响整个应用)
③ 有线程安全问题:不要在 forEach 中操作共享可变状态
④ 适合:大数据量的 CPU 密集型计算(如排序、统计)
⑤ 不适合:数据量小(线程切换开销 > 并行收益)、有序要求强的场景
plaintext
面试回答(2分钟版)

Lambda表达式是Java 8引入的匿名函数简写,它是函数式接口的实例。函数式接口是只有一个抽象方法的接口,Lambda通过类型推断自动匹配。Stream是基于Lambda的声明式集合操作API,采用三段式结构:数据源、中间操作和终端操作。中间操作如filter、map、flatMap都是惰性的,只是定义了操作管道但不立即执行;只有终端操作如collect、reduce、forEach触发时才真正执行整个管道。这种惰性求值的好处是短路操作可以提前终止,比如findFirst找到第一个就停。flatMap用于一对多映射后扁平化,是面试编码的高频操作。parallelStream底层使用ForkJoinPool的公共线程池,线程数默认是CPU核数减一,不适合IO密集型操作因为会阻塞公共线程池影响整个应用,适合大数据量的CPU密集型计算。Lambda捕获的外部变量必须是effectively final的,不能修改。

追问与易错

追问方向:

  • “Stream 是惰性求值的吗?”→ 是的,中间操作(filter/map/flatMap)只定义管道不立即执行,只有终端操作(collect/reduce/forEach)触发时才真正计算,支持短路优化
  • “parallelStream 有什么坑?”→ 底层共享 ForkJoinPool.commonPool(),IO 密集操作会阻塞公共线程池影响全局;forEach 中操作共享可变状态有线程安全问题;可能破坏元素有序性
  • “Lambda 捕获变量有什么限制?”→ 捕获的外部局部变量必须是 effectively final(事实上不可变),不能在 Lambda 内修改,因为 Lambda 捕获的是变量的副本而非引用

易错点:

  • ❌ “parallelStream 一定比串行快”——小数据量或简单操作反而慢(线程开销)
  • ❌ Stream 操作后原始集合不变——Stream 不修改源数据