flatMap 处理多对多关系的核心是逐层将“一个源对象→多个关联对象”展开为单一元素流,如 User→Stream→Stream,最终得到可操作的 Stream,支持去重、筛选等操作。

用 flatMap 处理多对多关系,核心是把“一个源对象 → 多个关联对象”的链路,逐层展开为单一元素流,再统一过滤、转换或聚合。它不直接支持“变量级联查询”这种数据库式语法,但能通过函数组合模拟出等效逻辑,关键在于映射函数返回的是 Stream 而非单个对象。
理解多对多在 Stream 中的建模方式
比如:一个用户(User)关联多个角色(Role),每个角色又对应多个权限(Permission)。这不是 User→Role→Permission 的三层嵌套集合,而是典型的“多对多”结构——即 User 和 Permission 之间通过 Role 中间表关联。
- 不能写成
users.stream().map(u -> u.getRoles()),那只会得到Stream<list>></list> - 正确路径是:User → Stream
→ Stream ,每一步都用 flatMap展开 - 最终结果是
Stream<permission></permission>,每个权限独立可操作,天然支持去重、按条件筛选、分组统计等
实现级联查询的典型链式写法
假设模型如下:
record User(String name, List<Role> roles) {}
record Role(String code, List<Permission> perms) {}
record Permission(String id, String action) {}
要查出所有用户拥有的全部权限(去重):
-
users.stream()—— 起始流:每个元素是 User -
.flatMap(u -> u.roles().stream())—— 第一层展开:每个 User 映射为多个 Role -
.flatMap(r -> r.perms().stream())—— 第二层展开:每个 Role 映射为多个 Permission -
.distinct().collect(Collectors.toList())—— 终端操作:扁平后直接去重收集
处理空值与中间集合缺失的安全写法
生产环境常见问题:某个 User.roles() 返回 null,或某个 Role.perms() 是空列表。直接调用 .stream() 会触发 NPE 或无意义空流。
- 对 null 集合防护:
.flatMap(u -> Optional.ofNullable(u.roles()).orElse(List.of()).stream()) - 跳过空集合更简洁:
.filter(u -> u.roles() != null && !u.roles().isEmpty()),再 flatMap - 如果 Permission 可能为 null,可在最后加
.filter(Objects::nonNull)
扩展:带条件的级联与字段投影
若只查 “admin 角色下的 write 权限”,可在展开过程中插入 filter:
.flatMap(u -> u.roles().stream().filter(r -> "admin".equals(r.code()))).flatMap(r -> r.perms().stream().filter(p -> p.action().contains("write")))- 这样整个链路就是“有约束的级联”,不是全量展开后再筛,性能更好
如需返回用户+权限组合(类似 SQL 的 JOIN 结果),可用 map 构造新 record:.map(p -> new UserPermission(u.name(), p.id(), p.action())),放在最后一层 flatMap 后即可。


















