flatMap是Java Stream中实现“一对多映射+自动扁平化”的核心算子,它接收Function<T, Stream<R>>函数,将每个元素映射为一个Stream<R>后合并为单一Stream<R>,适用于嵌套集合展平、对象关系展开及空安全处理等场景。

flatMap 是 Java Stream 中用于“一对多”映射并自动扁平化的关键操作,特别适合处理嵌套集合(如 List<List<T>>、List<Optional<T>> 或返回集合的函数逻辑)。
flatMap 的核心作用:先映射,再展平
它接收一个函数 Function<T, Stream<R>>,对每个元素生成一个 Stream<R>,然后把所有子流“无缝拼接”成一个单一的 Stream<R>。这与 map(只做一对一转换,结果仍是嵌套结构)有本质区别。
常见使用场景包括:
- 把
List<List<String>>转成单层Stream<String> - 从对象中提取多个关联项(如学生→课程列表)
- 过滤掉 null 或空集合(配合 Optional 或条件判断)
- 将字符串按分隔符拆分为单词流
基础示例:二维列表扁平化
假设有数据:List<List<Integer>> numbers = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3), Arrays.asList(4, 5, 6));
立即学习“Java免费学习笔记(深入)”;
用 flatMap 展开为单层整数流:
Stream<Integer> flattened = numbers.stream()
.flatMap(list -> list.stream()); // 每个子 List → Stream<Integer>,再合并
// 结果:1, 2, 3, 4, 5, 6
注意:list.stream() 返回的是 Stream<Integer>,flatMap 自动连接所有子流,无需手动调用 Stream.concat 或循环 collect。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
典型业务场景:对象关系展开
比如班级类包含多个学生,学生又选了多门课:
class Student { String name; List<String> courses; }
class ClassRoom { List<Student> students; }
List<ClassRoom> classrooms = ...;
// 获取所有学生所选课程的去重列表
List<String> allCourses = classrooms.stream()
.flatMap(room -> room.students.stream()) // 扁平化出所有 Student
.flatMap(student -> student.courses.stream()) // 再扁平化出所有课程名
.distinct()
.collect(Collectors.toList());
这里用了两次 flatMap —— 第一次解包班级到学生,第二次解包学生到课程,形成“链式扁平化”,清晰表达层级穿透逻辑。
进阶技巧:结合 Optional 和空安全处理
当字段可能为 null 或需条件过滤时,可返回空流跳过该元素:
List<Student> students = ...;
List<String> validNames = students.stream()
.flatMap(s -> Optional.ofNullable(s)
.filter(stu -> stu.name != null && !stu.name.trim().isEmpty())
.map(stu -> Stream.of(stu.name))
.orElseGet(Stream::empty))
.collect(Collectors.toList());
等价更简洁写法(推荐):
.flatMap(s -> s == null || s.name == null ? Stream.empty()
: Stream.of(s.name.trim()))
关键是:只要函数返回 Stream<R>,无论非空、空(Stream.empty())还是单元素(Stream.of(x)),flatMap 都能统一处理。
对比 map + flatMap:为什么不能只用 map?
如果误用 map:
numbers.stream().map(list -> list.stream()) // 类型是 Stream<Stream<Integer>>,不是 Stream<Integer> // 后续无法直接 .forEach(System.out::println),会打印 Stream 对象地址
必须再加一层 flatMap 或手动遍历,失去函数式简洁性。flatMap 就是为此而生——一步到位。

















