发布于2026-07-09 阅读(0)
扫一扫,手机访问
嵌套的层级数据,在家谱、组织架构图、评论回复链里随处可见。怎么把这些数据在Ja va里优雅地建模?怎么又卡壳了?其实核心就一个题型:递归构造对象树。处理方式也不复杂,先创建当前节点,再递归构建它的子树,最后把父与子组装起来,整个链条就清晰了。
下面直接上一套可以落地实现的方案,从模型定义到递归方法,再到实际的API回调集成,一步到位。
static class Person {
String name;
int age;
List children;
// 无子节点构造器
Person(String name, int age) {
this.name = name;
this.age = age;
this.children = null;
}
// 带子节点构造器(推荐用于反序列化)
Person(String name, int age, List children) {
this.name = name;
this.age = age;
this.children = children;
}
}
有一点值得注意:
children字段最好允许为null,而不是空列表。这么做能跟原始数据的语义保持严格一致——没有子节点就是没有,不产生冗余对象,后续逻辑里判断起来也更直观。
// 全局容器:存储所有已构建的 Person 实例(按需保留) private final ListallPersons = new ArrayList<>(); /** * 递归遍历并构建 Person 树 * @param persons 当前层级的 Person 列表(可能为空或 null) */ private void buildPersonTree(List persons) { if (persons == null || persons.isEmpty()) return; for (Person person : persons) { // 1. 将当前 person 加入全局列表(如需扁平化访问) allPersons.add(person); // 2. 递归构建其子树 if (person.children != null) { buildPersonTree(person.children); } } }
这段代码其实很直白:遍历当前层级的每个节点,存到全局容器,然后对它的孩子做同样的事情。没有花哨的技巧,胜在可读性强、逻辑清晰。
private void loadPersons(String id) {
ListPersonsApi.Request request = Jso.create();
request.id = id;
dispatcher.send(ListPersonsApi.PATH, request, r -> {
// 步骤1:将原始 API 数据映射为 Person 对象树(保持嵌套结构)
List rootPersons = new ArrayList<>();
for (ListPersonsApi.Person apiPerson : r.persons) {
// 递归转换 children 字段(关键!)
List convertedChildren = convertChildren(apiPerson.children);
rootPersons.add(new Person(apiPerson.name, apiPerson.age, convertedChildren));
}
// 步骤2:启动递归构建,填充 allPersons 并建立完整树形关系
buildPersonTree(rootPersons);
});
}
// 辅助方法:将 API 的 children 数组递归转为 Person 列表
private List convertChildren(List apiChildren) {
if (apiChildren == null || apiChildren.isEmpty()) return null;
List children = new ArrayList<>();
for (ListPersonsApi.Person child : apiChildren) {
List grandChildren = convertChildren(child.children);
children.add(new Person(child.name, child.age, grandChildren));
}
return children;
}
整个过程分两步走:先把API返回的原始数据递归转换成Person对象树,再调用buildPersonTree跑一遍递归填充。这个convertChildren方法虽然是个辅助角色,却是整个树形结构正确性最大的保障——它保证了一个父亲的儿子们,确实挂在了正确的位置上。
假设API返回了下面这样一份JSON:
{
"persons": [
{"name":"John","age":18,"children":null},
{"name":"Lisa","age":32,"children":[{"name":"Tyler","age":7,"children":null}]},
{"name":"Mike","age":90,"children":[{"name":"Derek","age":50,"children":[{"name":"Mary","age":25,"children":null},{"name":"Beth","age":16,"children":null}]}]}
]
}执行之后,allPersons会得到7个Person实例。你只需要验证一件事:Mike.children.get(0).children这个链,是否正确指向了Mary和Beth。如果答案是肯定的,那整个树形结构的引用关系就完美建立起来了。
children == null,而不是children.isEmpty()。null代表“本就没有孩子”,空列表代表“有孩子这个字段,但列表是空的”。语义不同,处理方式也会跟着不同。allPersons砍掉,直接返回根节点列表就行,省一点内存是一点。StackOverflowError的风险。这时候可以考虑用栈模拟递归,不过话说回来,绝大多数业务场景里递归的深度远远够用,不用为未来过度焦虑。总的来说,这套设计兼顾了可读性、扩展性和数据对齐,后期无论是做渲染、搜索还是编辑,底层模型都不会拖后腿。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8