概要
树形结构数据是后台开发中非常常见的一种数据结构。后台管理系统中必要的一种结构,常见的树形结构数据有,部门树,权限树等等,利用该数据结构可以让你的系统数据展示一目了然,并且为分配这些数据也提供了极大地便利。如图所示的是菜单树。
菜单选择树:
生成前提
这样的树通常看起来容易实现,但是实际生成有一定的难度,尤其是没有顶级节点时(一共n个节点,其中n-1个节点直接或者间接挂在一个节点上的情况),代码稍稍有点复杂。生成这样的一棵树,必须满足节点有id(节点唯一标识符),pid(父节点的唯一标识符)和子节点集合。用java类可以描述为:
@Data
public class TreeNode {
/**
* 唯一标识符
*/
private Long key;
/**
* 节点描述
*/
private String title;
/**
* 父节点唯一标识符
*/
private Long parentId;
/**
* 子节点集合
*/
private List<TreeNode> children;
}
注意: 其中的@Data是Lombok提供的注解,提供了字段的get、set、equals、hashCode、canEqual、toString等方法的生成。如果不了解的同学,自行百度。
生成代码
如果不漏掉所有的节点,需要先找出所有的父节点
public class Tree {
List<TreeNode> nodes;
public Tree(List<TreeNode> nodes) {
this.nodes = nodes;
}
/**
* 构建树形结构
*/
public List<TreeNode> buildTree() {
List<TreeNode> treeNodes = new ArrayList<>();
List<TreeNode> rootNodes = getRootNodes();
for (TreeNode rootNode : rootNodes) {
buildChildNodes(rootNode);
treeNodes.add(rootNode);
}
return treeNodes;
}
/**
* 递归子节点
*/
private void buildChildNodes(TreeNode node) {
List<TreeNode> children = getChildNodes(node);
if (!children.isEmpty()) {
for (TreeNode child : children) {
buildChildNodes(child);
}
node.setChildren(children);
}
}
/**
* 获取父节点下所有的子节点
*/
public List<TreeNode> getChildNodes(TreeNode pNode) {
List<TreeNode> childNodes = new ArrayList<>();
for (TreeNode n : nodes) {
if (pNode.getKey().equals(n.getParentId())) {
childNodes.add(n);
}
}
return childNodes;
}
/**
* 判断是否为根节点
*/
public boolean rootNode(TreeNode node) {
boolean isRootNode = true;
for (TreeNode n : nodes) {
if (node.getParentId().equals(n.getKey())) {
isRootNode = false;
break;
}
}
return isRootNode;
}
/**
* 获取集合中所有的根节点
*/
public List<TreeNode> getRootNodes() {
List<TreeNode> rootNodes = new ArrayList<>();
for (TreeNode n : nodes) {
if (rootNode(n)) {
rootNodes.add(n);
}
}
return rootNodes;
}
}
说明
从上面代码中可以看到,主要有两个重要的步骤,一、获取集合中的所有的根节点;二、递归子节点。
使用
不难看出该类只有一个构造器,并且构造的时候需要传入节点集合。
public class Test {
public static void main(String[] args) {
// 构造节点集合,通常是数据库中查出
List<TreeNode> list = new ArrayList<>();
Tree tree = new Tree(list);
List<TreeNode> treeNodes = tree.buildTree();
}
}
通常这个生成之后,数据就可以交由前端去渲染。可供常见的前端框架antd,iview等使用,具体情况具体修改。生成已经有了,但是如果只有子节点,如果完整渲染出仅包含该子节点的树呢?其实思路也是和这个类似的