TreeNode用来表示每个树节点的抽象,即需要生成树的对象需要实现此接口。
public interface TreeNode<T> {
/**
* 获取节点id
*
* @return 树节点id
*/
T id();
/**
* 获取该节点的父节点id
*
* @return 父节点id
*/
T parentId();
/**
* 是否是根节点
*
* @return true:根节点
*/
boolean root();
/**
* 设置节点的子节点列表
*
* @param children 子节点
*/
void setChildren(List<? extends TreeNode<T>> children);
/**
* 获取所有子节点
*
* @return 子节点列表
*/
List<? extends TreeNode<T>> getChildren();
}
TreeUtils用来生成树形结构,以及获取所有叶子节点等操作
public class TreeUtils {
/**
* 根据所有树节点列表,生成含有所有树形结构的列表
*
* @param nodes 树形节点列表
* @param <T> 节点类型
* @return 树形结构列表
*/
public static <T extends TreeNode<?>> List<T> generateTrees(List<T> nodes) {
List<T> roots = new ArrayList<>();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (node.root()) {
roots.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
roots.forEach(r -> setChildren(r, nodes));
return roots;
}
/**
* 从所有节点列表中查找并设置parent的所有子节点
*
* @param parent 父节点
* @param nodes 所有树节点列表
*/
@SuppressWarnings("all")
public static <T extends TreeNode> void setChildren(T parent, List<T> nodes) {
List<T> children = new ArrayList<>();
Object parentId = parent.id();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (Objects.equals(node.parentId(), parentId)) {
children.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
// 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
if (children.isEmpty()) {
return;
}
parent.setChildren(children);
children.forEach(m -> {
// 递归设置子节点
setChildren(m, nodes);
});
}
/**
* 获取指定树节点下的所有叶子节点
*
* @param parent 父节点
* @param <T> 实际节点类型
* @return 叶子节点
*/
public static <T extends TreeNode<?>> List<T> getLeafs(T parent) {
List<T> leafs = new ArrayList<>();
fillLeaf(parent, leafs);
return leafs;
}
/**
* 将parent的所有叶子节点填充至leafs列表中
*
* @param parent 父节点
* @param leafs 叶子节点列表
* @param <T> 实际节点类型
*/
@SuppressWarnings("all")
public static <T extends TreeNode> void fillLeaf(T parent, List<T> leafs) {
List<T> children = parent.getChildren();
// 如果节点没有子节点则说明为叶子节点
if (children == null || children.isEmpty()) {
leafs.add(parent);
return;
}
// 递归调用子节点,查找叶子节点
for (T child : children) {
fillLeaf(child, leafs);
}
}
}
构建自己的dto
@Data
@NoArgsConstructor
public class MenuVo implements TreeNode<Long>{
private Long id;
private Long pid;
private Integer type;
private String name;
private String icon;
private String code;
private Integer status;
private List<MenuVo> children;
@Override
public Long id() {
return this.id;
}
@Override
public Long parentId() {
return this.pid;
}
@Override
public boolean root() {
return Objects.equals(this.pid, 0L);
}
@Override
public void setChildren(List children) {
this.children = children;
}
public MenuVo(Long id, String name, Long pid) {
this.id = id;
this.pid = pid;
this.name = name;
}
public MenuVo(Long id, Long pid, Integer type, String name, String icon, String code, Integer status, List<MenuVo> children) {
this.id = id;
this.pid = pid;
this.type = type;
this.name = name;
this.icon = icon;
this.code = code;
this.status = status;
this.children = children;
}
}
测试类
public class MenoTest {
public static void main(String[] args) {
List<MenuVo> menuList = new ArrayList<>();
menuList.add(new MenuVo(1L, "根节点", 0L));
menuList.add(new MenuVo(2L, "第二层级节点2", 1L));
menuList.add(new MenuVo(3L, "第二层级节点3", 1L));
menuList.add(new MenuVo(4L, "第三层级节点4", 2L));
menuList.add(new MenuVo(5L, "第三层级节点5", 2L));
menuList.add(new MenuVo(6L, "第四层级节点6", 4L));
menuList.add(new MenuVo(7L, "第四层级节点7", 5L));
menuList.add(new MenuVo(8L, "第五层级节点8", 7L));
List<MenuVo> menuVos = TreeUtils.generateTrees(menuList);
System.out.println(JSONUtil.toJsonPrettyStr(menuVos));
}
}
结果如下
评论区