如何使用 Java Stream API 扁平化嵌套集合并构建组合对象

本文详解如何利用 `flatmap` 与嵌套 `stream()` 链式调用,将三层嵌套结构(c → list → list)一次性转换为扁平化的 `list`,避免传统 for 循环,提升代码表达力与函数式编程能力。

在 Java 11 及以上版本中,面对具有层级关系的嵌套数据结构(如 C 持有 List,每个 B 又持有 List),若需生成所有 (A, B, C) 属性组合的扁平列表(如 List),核心挑战在于:如何在单一流水线中跨越多层集合边界,同时捕获各层级的非集合字段(如 c1, c2, b1, a1)

答案是:flatMap

是关键。它用于将“流中的每个元素映射为一个新流,再将这些流合并为一个扁平流”,完美匹配“对每个 B,展开其全部 A”这一语义。

以下为完整、可直接使用的 Stream 实现:

C c = someGetterForC();

List dList = c.getCList()                           // Stream
                 .stream()
                 .flatMap(b -> b.getBList()            // 对每个 B,获取其 List
                                 .stream()             // 转为 Stream
                                 .map(a -> new D(      // 将每个 A 与上下文 B、C 字段组合
                                     a.getA1(),        // 来自 A
                                     b.getB1(),        // 来自当前 B
                                     c.getC1(),        // 来自顶层 C(闭包捕获)
                                     c.getC2()         // 来自顶层 C(闭包捕获)
                                 )))
                 .collect(Collectors.toList());

为什么这样可行?

  • c.getCList().stream() 创建 B 的流;
  • flatMap(b -> ...) 对每个 b 执行一个函数,该函数返回 Stream(由 b.getBList().stream().map(...) 构建);
  • flatMap 自动将所有 Stream 合并为单一 Stream,实现跨 B 边界的扁平化;
  • 最终 collect(Collectors.toList()) 汇总结果。

⚠️ 注意事项

  • c.getC1() 和 c.getC2() 在 lambda 中被有效闭包捕获——因 c 是方法局部变量且未被修改,符合 Java 的“实际上的 final”要求,安全可用;
  • flatMap 不可替换为 map:map 会生成 Stream>,导致类型不匹配;
  • 若某 B 的 bList 为 null,需提前防护(如 Objects.requireNonNull(b.getBList()) 或使用 Optional.ofNullable(b.getBList()).orElse(Collections.emptyList()));
  • 性能上,Stream 版本与双层 for 循环时间复杂度一致(O(n×m)),无额外开销,但可读性与维护性更优。

? 进阶提示(Java 11 兼容)
若需空安全或更清晰的链式表达,可封装为工具方法:

public static List flattenToD(C c) {
    return Optional.ofNullable(c)
            .map(C::getCList)
            .stream()
            .flatMap(list -> list.stream())
            .flatMap(b -> Optional.ofNullable(b.getBList())
                    .stream()
                    .flatMap(innerList -> innerList.stream())
                    .map(a -> new D(a.getA1(), b.getB1(), c.getC1(), c.getC2())))
            .collect(Collectors.toList());
}

综上,通过合理运用 stream() + flatMap() + map() 的三级组合,即可优雅、高效、一次性地完成深层嵌套数据的解构与重组,真正践行函数式数据处理思想。