概要

树形结构数据是后台开发中非常常见的一种数据结构。后台管理系统中必要的一种结构,常见的树形结构数据有,部门树,权限树等等,利用该数据结构可以让你的系统数据展示一目了然,并且为分配这些数据也提供了极大地便利。如图所示的是菜单树。

java 实现树形结构 java实现树结构存储数据_数据


菜单选择树:

java 实现树形结构 java实现树结构存储数据_子节点_02

生成前提

这样的树通常看起来容易实现,但是实际生成有一定的难度,尤其是没有顶级节点时(一共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等使用,具体情况具体修改。生成已经有了,但是如果只有子节点,如果完整渲染出仅包含该子节点的树呢?其实思路也是和这个类似的