
本文介绍一种基于 java 8 stream 的简洁、函数式方法,用于递归遍历任意深度的树形结构对象(如嵌套的 message 实例),并将其扁平化为单层 list,无需第三方库,纯 jdk 实现。
本文介绍一种基于 java 8 stream 的简洁、函数式方法,用于递归遍历任意深度的树形结构对象(如嵌套的 message 实例),并将其扁平化为单层 list,无需第三方库,纯 jdk 实现。
在处理具有自引用嵌套关系的对象(例如树形结构)时,常见的需求是将根节点及其所有后代节点一次性收集到一个扁平列表中。以 Message 类为例——它包含 id、status 和 List<Message> children 字段,而每个子消息又可拥有自己的子消息,形成无限递归层级。传统做法常依赖显式递归方法或栈/队列遍历,但借助 Java Stream API,我们可以写出更清晰、不可变且易于组合的解决方案。
核心思路是:为 Message 类添加一个 children() 方法,该方法返回一个 Stream<Message>,流中包含当前节点自身 + 所有后代节点(即“包含自身的深度优先遍历流”)。实现采用 Stream.concat() 与 flatMap() 组合,体现典型的递归流构造模式:
public Stream<Message> children() {
Stream<Message> selfAndDirect = Stream.of(this);
if (children == null || children.isEmpty()) {
return selfAndDirect;
}
return Stream.concat(
selfAndDirect,
children.stream().flatMap(Message::children)
);
}✅ 关键说明:
- Stream.of(this) 确保根节点被包含(符合“获取所有 children(含自身)”的常见语义;若仅需纯后代,可改为 Stream.empty() 并单独处理);
- flatMap(Message::children) 将每个子节点的递归流“摊平”进顶层流,避免嵌套 Stream;
- 空集合保护(children == null || isEmpty())提升健壮性,防止 NullPointerException 或空流重复拼接。
使用时极为简洁:
立即学习“Java免费学习笔记(深入)”;
List<Message> allMessages = rootMessage.children().collect(Collectors.toList());
// 或转为其他集合
Set<Message> uniqueMessages = rootMessage.children()
.collect(Collectors.toCollection(LinkedHashSet::new));⚠️ 注意事项:
- 该方法不修改原对象状态,完全无副作用,符合函数式编程原则;
- 由于使用递归调用 flatMap,极端深度(如 >10,000 层)可能引发 StackOverflowError —— 若存在超深树场景,建议改用基于 Deque 的迭代式 BFS/DFS 实现;
- 若需控制遍历顺序(如广度优先),Stream 方案天然不支持(因 flatMap 是深度优先),此时应选用显式队列实现;
- 若 Message 类不可修改(如来自外部库),可封装为工具类静态方法,例如 TreeUtils.flatten(message),内部逻辑保持一致。
总结而言,该方案以极简代码实现了树形结构的声明式扁平化,在绝大多数业务场景(如评论回复树、组织架构、菜单导航等)中兼具可读性、可维护性与性能表现,是 Java 函数式编程实践的典型范例。










