flatMap用于二度人脉统计的核心是将“用户→一度好友→一度好友的好友”嵌套关系拉平为单层流,再过滤自身及一度好友、去重并计数;关键在逻辑准确与数据预加载优化。

直接用 flatMap 做二度人脉统计,核心是把“用户 → 一度好友 → 一度好友的好友”这层嵌套关系拉平,再过滤掉自己和一度好友,最后聚合去重计数。关键不在写法多炫,而在逻辑是否准确、边界是否清晰。
明确数据结构和目标
假设你有如下模型:
-
User:含
id和List<Long> directFriends(一度好友 ID 列表) - 目标:对指定用户
targetId,统计其所有二度好友(即一度好友的好友中,排除自己和一度好友)的出现频次
用 flatMap 展开两层关系
第一步不是统计,而是把“一度好友的好友”全部摊开成一条扁平流:
List<Long> secondDegreeIds = users.stream()
.filter(u -> u.id == targetId)
.findFirst()
.map(user -> user.directFriends.stream() // 一度好友列表 → Stream<Long>
.flatMap(friendId -> findUserById(friendId) // 查一度好友对象
.map(friend -> friend.directFriends.stream()) // 取他的一度好友(即 target 的二度)
.orElse(Stream.empty()))
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
⚠️ 注意:findUserById 需高效(建议预加载为 Map<Long, User>),否则 N² 查询会拖垮性能。
去重 + 排除自身与一度好友
拿到所有候选二度 ID 后,要剔除无效项:
- 剔除
targetId自身 - 剔除所有一度好友 ID(避免重复计算或混淆关系层级)
- 去重后按出现次数排序
完整链式写法:
Map<Long, Long> secondDegreeCount = users.stream()
.filter(u -> u.id == targetId)
.findFirst()
.map(target -> {
Set<Long> directSet = new HashSet<>(target.directFriends);
return target.directFriends.stream()
.flatMap(fid -> findUserById(fid)
.map(friend -> friend.directFriends.stream())
.orElse(Stream.empty()))
.filter(id -> id != target.id && !directSet.contains(id)) // 排除自己和一度
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
})
.orElse(Collections.emptyMap());
大规模场景下的关键优化点
当用户量达百万级时,纯内存 Stream 容易 OOM 或变慢,需配合以下策略:
-
预建反向索引:构建
Map<Long, List<Long>> inverseIndex,即“谁的好友包含我”,可快速反查二度来源 - 分批处理:对一度好友列表切片,每批 100–500 个,避免单次 flatMap 拉出过长流
-
用 IntStream 或并行流谨慎:仅当好友关系稀疏且无状态依赖时启用
parallelStream(),否则容易引发线程安全问题
二度人脉统计本质是图的 2-hop 遍历,flatMap 提供了声明式表达能力,但真正决定效率的是数据组织方式和查询路径设计。


















