我们从数据库中查出来的数据是一个List,如何把这个平数组转换成树形结构呢?假定有如下实体:
@Data
public class Category {
private Long id;
private String title;
private Long parentId;
private List<Category> children;
}
从数据库查出来的只有id
,title
,parentId
,我们要利用parentId
把父子关系组织起来(顶层 Category
的 parentId
为 null
)。先上代码:
private List<Category> buildTree(List<Category> all) {
for (Category curr : all) {
Long pid = curr.getParentId();
Optional<Category> parentOptional = all.stream().filter(ca -> ca.getId().equals(pid)).findFirst();
if (parentOptional.isPresent()) { // 相当于 !parentOptional.isEmpty()
Category p = parentOptional.get();
if (null == p.getChildren()) {
p.setChildren(new ArrayList<>());
}
p.getChildren().add(curr);
}
}
return all.stream().filter(ca -> ca.getParentId() == null).toList();
}
- 上述代码主要在遍历里用了 List 的 stream() 相关接口,快速找到当前这一条记录的父节点,然后把当前这一条记录放到其父节点的 children 中。
- 因为对象是地址引用,这里一个 Category 是先被当作父节点处理(在其中插入子节点)还是被当作子节点处理(插入到另一个节点的children中)并不影响最终结果。
- 同样因为对象是地址引用,即使一个对象被插入到另一个对象的 children 中了,本来的平数组(all),里面一样保留了一份引用,这样这个对象还在遍历中,后续还是会被处理到的。
- 所以逻辑就变得非常简单,一言以敝之,每个节点找到自己的父节点即可。
- 最后,把所有父节点为
null
的都找出来。它们是顶层节点,拔出萝卜带出泥,全部节点就出来了。 - 这个算法的优点是对原来平数组中的节点顺序没有要求,而其他有些算法需要保证父节点先出现,有些算法需要保证子节点先出现。