flatMap是Java Stream中处理“一对多”映射并自动扁平化的核心方法,它对每个元素返回一个Stream再合并为单一流,适用于List<List<T>>、对象含集合属性等嵌套场景,要求mapper函数必须返回Stream且需防范null值。

flatMap 是 Java Stream 中专门用于“一对多”映射并自动扁平化结果的核心方法,特别适合处理多层嵌套集合(比如 List<List<String>>、List<Person> 中每个 Person 有多个 Phone 等场景)。
flatMap 的核心逻辑:先映射,再拆箱
不同于 map(一对一转换,返回单个新元素),flatMap 接收一个函数,该函数对每个元素返回一个 Stream,然后把所有这些 Stream “连成一条”——也就是把多层结构压成一层。
关键点:
- 输入:一个元素 → 输出:一个
Stream<T>(不能是普通集合或数组) - flatMap 会自动将每个子 Stream 的元素“摊开”,合并为最终的单一 Stream
- 如果传入
null流,会抛出NullPointerException;空 Stream(如Stream.empty())则安全跳过
常见嵌套场景与写法示例
1. List> → List
立即学习“Java免费学习笔记(深入)”;
这是最典型的二维扁平化:
List<List<String>> nested = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c"),
Arrays.asList("d", "e", "f")
);
List<String> flat = nested.stream()
.flatMap(list -> list.stream()) // 每个 inner List → Stream<String>
.collect(Collectors.toList());
// 结果:["a", "b", "c", "d", "e", "f"]
2. 对象中含集合属性 → 展开全部子项
比如用户有多个邮箱,想获取所有用户的全部邮箱地址:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
record User(String name, List<String> emails) {}
List<User> users = List.of(
new User("Alice", List.of("a1@x.com", "a2@y.com")),
new User("Bob", List.of("b1@z.com"))
);
List<String> allEmails = users.stream()
.flatMap(u -> u.emails().stream()) // 每个 User → Stream<String>(其 emails)
.collect(Collectors.toList());
3. 处理可能为空的集合(避免 NPE)
实际业务中子集合可能是 null,需主动防护:
.flatMap(u -> Optional.ofNullable(u.emails())
.orElse(Collections.emptyList())
.stream())
或者更简洁地用 Collection::stream 配合空判断:
.flatMap(u -> u.emails() == null ? Stream.empty() : u.emails().stream())
flatMap 和 map + flatMap 的区别
如果误用 map 处理嵌套集合,会得到 Stream<Stream<T>>,必须再调一次 flatMap 才能扁平化:
// ❌ 错误:得到 Stream<Stream<String>> stream.map(list -> list.stream()) // ✅ 正确:一步到位 stream.flatMap(list -> list.stream())
记住口诀:要展开,就用 flatMap;只转换,才用 map。
进阶:多级嵌套(三层及以上)怎么扁平化?
flatMap 本身只做“一层扁平”,但可以链式调用实现深度展开。例如 List<List<List<Integer>>>:
List<List<List<Integer>>> threeLevel = ...;
List<Integer> flattened = threeLevel.stream()
.flatMap(first -> first.stream()) // 层1 → 层2
.flatMap(second -> second.stream()) // 层2 → 层3(最终元素)
.collect(Collectors.toList());
每调一次 flatMap,就“剥掉一层包装”,链式次数 = 嵌套层数 − 1。

















