ETJava Beta | Java    注册   登录
  • 搜索:
  • 树形结构工具类

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/huanzi-qch/p/18431505
    如有侵权 请联系我们删除  (页面底部联系我们)  

      前言

      日常开发中,树形结构的数据是比较常见的一种数据结构,比如系统菜单、组织机构、数据字典等,有时候需要后端把数据转成树形结构再返回给前端,对此特意封装通用树形结构工具类

      封装了以下方法:

      根据父id,递归获取所有子节点,转为树结构

     

      根据子id,递归获取所有父节点,转为树结构

     

      拼接 union sql脚本,根据查询查询条件、id字段名、pid字段名,拼接出sql

       

      依赖hutool

            <!-- hutool -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.18</version>
            </dependency>

     

      完整代码

    package cn.huanzi.qch.util;
    
    import cn.hutool.core.bean.BeanUtil;
    
    import java.util.ArrayList;
    import java.util.Comparator;
    import java.util.List;
    
    /**
     * 树形结构工具类
     */
    public class TreeUtil{
    
        /**
         * 根据父id,递归获取所有子节点,转为树结构
         *
         * @param idFieldName id字段名称
         * @param pIdFieldName pid字段名称
         * @param childrenFieldName children字段名称
         * @param pxFieldName px字段名称
         * @param pId 父节点id
         * @param allList 所有菜单列表
         * @return 每个根节点下,所有子菜单列表
         */
        public static <M>  List<M> toTreeByParentId(String idFieldName, String pIdFieldName, String childrenFieldName, String pxFieldName,String pId, List<M> allList){
            //子节点
            List<M> childList = new ArrayList<>(allList.size());
            for (int i = 0; i < allList.size(); i++) {
                M model = allList.get(i);
    
                //遍历所有节点将节点的父id与传过来的根节点的id比较
                //父节点id字段,例如:pid
                if (BeanUtil.getFieldValue(model,pIdFieldName).equals(pId)){
                    childList.add(model);
    
                    //删除,减少下次循环次数
                    allList.remove(i);
                    i--;
                }
            }
            //递归
            for (M model : childList) {
                //主键字段,例如:id,子节点字段,例如:children
                BeanUtil.setFieldValue(model,childrenFieldName,TreeUtil.toTreeByParentId(idFieldName,pIdFieldName,childrenFieldName,pxFieldName,String.valueOf(BeanUtil.getFieldValue(model,idFieldName)), allList));
            }
    
            //排序字段,例如:px,如果不需要排序可以注释
            if(null != pxFieldName && !pxFieldName.isEmpty()){
                childList.sort(Comparator.comparingInt(m -> Integer.parseInt(String.valueOf(BeanUtil.getFieldValue(m, pxFieldName))))); //排序
            }
    
            //底层节点的子节点赋空值,节省内存空间
            if(childList.size() <= 0){
                childList = null;
            }
    
            return childList;
        }
        public static <M>  List<M> toTreeByParentId(String pId, List<M> allList){
            //设置一下默认值
            return TreeUtil.toTreeByParentId("id","pid","children","px",pId,allList);
        }
    
        /**
         * 根据子id,递归获取所有父节点,转为树结构
         *
         * @param idFieldName id字段名称
         * @param pIdFieldName pid字段名称
         * @param childrenFieldName children字段名称
         * @param cId 子节点id
         * @param allList 所有菜单列表
         * @return 每个根节点下,所有子菜单列表
         */
        public static <M> M toTreeByChildrenId(String idFieldName, String pIdFieldName, String childrenFieldName,String cId, List<M> allList){
            return TreeUtil.toTreeByChildrenId(idFieldName,pIdFieldName,childrenFieldName,null,cId,allList);
        }
        private static <M> M toTreeByChildrenId(String idFieldName, String pIdFieldName, String childrenFieldName,M parent,String cId, List<M> allList){
            //父节点
            M newParent = null;
    
            for (int i = 0; i < allList.size(); i++) {
                M model = allList.get(i);
    
                // 相等说明:找出当前节点
                if (BeanUtil.getFieldValue(model,idFieldName).equals(cId)){
                    newParent = model;
    
                    //设置子节点
                    if(parent != null){
                        ArrayList<M> childList = new ArrayList<>(1);
                        childList.add(parent);
                        BeanUtil.setFieldValue(newParent,childrenFieldName, childList);
    
                    }
    
                    //删除,减少下次循环次数
                    allList.remove(i);
                    i--;
                    break;
                }
            }
    
            //父节点为空,则说明为顶层节点
            String menuParentId = newParent == null ? "" : String.valueOf(BeanUtil.getFieldValue(newParent,pIdFieldName));
            if("".equals(menuParentId)){
                return parent;
            }
    
            //父节点递归
            newParent = TreeUtil.toTreeByChildrenId(idFieldName,pIdFieldName,childrenFieldName,newParent,menuParentId,allList);
    
            return newParent;
        }
        public static <M> M toTreeByChildrenId(String cId, List<M> allList){
            //设置一下默认值
            return TreeUtil.toTreeByChildrenId("id", "pid", "children",null,cId,allList);
    
        }
    
        /**
         * 拼接 union sql脚本,根据查询查询条件、id字段名、pid字段名,拼接出sql
         * @param select 查询字段,例如: select id
         * @param tableName 表名,例如 sys_dept
         * @param initWhere 查询条件,例如:pid = '-1'
         * @param idField id字段名
         * @param pidField pid字段名
         * @param maxLevel 拼接最大层级
         * @return union拼接好的sql脚本
         */
        public static String getParentSql(String select, String tableName, String initWhere, String idField, String pidField, int maxLevel) {
            return getUnionSql(select,tableName,initWhere,idField,pidField,maxLevel);
        }
        public static String getChildSql(String select, String tableName, String initWhere, String idField, String pidField, int maxLevel) {
            return getUnionSql(select,tableName,initWhere,pidField,idField,maxLevel);
        }
        private static String getUnionSql(String select, String tableName, String initWhere, String whereIn, String selectIn, int maxLevel) {
            StringBuilder stringBuilder = new StringBuilder(select);
            stringBuilder.append(" from ").append(tableName).append(" where 1=1 ");
            if (null != initWhere && !initWhere.isEmpty()) {
                stringBuilder.append(" and ").append(initWhere);
            }
    
            String tmp = String.format("from %s where %s", tableName, initWhere);
    
            for(int i = 0; i < maxLevel; ++i) {
                tmp = String.format(" from %s where %s in ( select %s %s)", tableName, whereIn, selectIn, tmp);
                stringBuilder.append(" union ").append(select).append(tmp);
            }
    
            return stringBuilder.toString();
        }
    
    }
    TreeUtil

     

      完整main测试

        /**
         * 测试
         */
        public static void main(String[] args) {
            ArrayList<Menu> list = new ArrayList<>();
            list.add(new Menu("1","-1","系统管理",1));
            list.add(new Menu("11","1","菜单维护",1));
            list.add(new Menu("12","1","角色维护",2));
            list.add(new Menu("13","1","系统安全",3));
            list.add(new Menu("131","13","日志管理",1));
            list.add(new Menu("12","-1","用户管理",2));
    
            //备份list
            List<Menu> list1 = BeanUtil.copyToList(list, Menu.class);
    
            List<Menu> menus = TreeUtil.toTreeByParentId("-1", list);
            System.out.println(menus);
    
            Menu menu = TreeUtil.toTreeByChildrenId("131", list1);
            System.out.println(menu);
    
            String sql = TreeUtil.getChildSql("select id", "lp_sys_menu", "pid = '-1'", "id", "pid", 3);
            System.out.println(sql);
        }
    
        /**
         * 测试菜单类
         */
        public static class Menu {
            private String id; //节点id
            private String pid; //父节点id
            private List<Menu> children; //子节点
            private int px; //排序字段
            private String menuName; //菜单名称
    
            public Menu() {
            }
    
            public Menu(String id, String pid, String name, int px) {
                this.id = id;
                this.pid = pid;
                this.menuName = name;
                this.px = px;
            }
    
            public String getId() {
                return id;
            }
    
            public void setId(String id) {
                this.id = id;
            }
    
            public String getPid() {
                return pid;
            }
    
            public void setPid(String pid) {
                this.pid = pid;
            }
    
            public List<Menu> getChildren() {
                return children;
            }
    
            public void setChildren(List<Menu> children) {
                this.children = children;
            }
    
            public int getPx() {
                return px;
            }
    
            public void setPx(int px) {
                this.px = px;
            }
    
            public String getMenuName() {
                return menuName;
            }
    
            public void setMenuName(String menuName) {
                this.menuName = menuName;
            }
    
            @Override
            public String toString() {
                return "Menu{" +
                        "id='" + id + '\'' +
                        ", pid='" + pid + '\'' +
                        ", children=" + children +
                        ", px=" + px +
                        ", menuName='" + menuName + '\'' +
                        '}';
            }
    
        }
    View Code

     

      效果展示

    [Menu{id='1', pid='-1', children=[Menu{id='11', pid='1', children=null, px=1, menuName='菜单维护'}, Menu{id='12', pid='1', children=null, px=2, menuName='角色维护'}, Menu{id='13', pid='1', children=[Menu{id='131', pid='13', children=null, px=1, menuName='日志管理'}], px=3, menuName='系统安全'}], px=1, menuName='系统管理'}, Menu{id='12', pid='-1', children=null, px=2, menuName='用户管理'}]
    Menu{id='1', pid='-1', children=[Menu{id='13', pid='1', children=[Menu{id='131', pid='13', children=null, px=1, menuName='日志管理'}], px=3, menuName='系统安全'}], px=1, menuName='系统管理'}
    select id from lp_sys_menu where 1=1  and pid = '-1' union select id from lp_sys_menu where pid in ( select id from lp_sys_menu where pid = '-1') union select id from lp_sys_menu where pid in ( select id  from lp_sys_menu where pid in ( select id from lp_sys_menu where pid = '-1')) union select id from lp_sys_menu where pid in ( select id  from lp_sys_menu where pid in ( select id  from lp_sys_menu where pid in ( select id from lp_sys_menu where pid = '-1')))

     

      原数据

       根据父id,递归获取所有子节点,转为树结构

       根据子id,递归获取所有父节点,转为树结构

       拼接 union sql脚本,根据查询查询条件、id字段名、pid字段名,拼接出sql

    SELECT
        id
    FROM
        lp_sys_menu
    WHERE
        1 = 1
        AND pid = '-1'
    UNION
    SELECT
        id
    FROM
        lp_sys_menu
    WHERE
        pid IN (
            SELECT
                id
            FROM
                lp_sys_menu
            WHERE
                pid = '-1'
        )
    UNION
    SELECT
        id
    FROM
        lp_sys_menu
    WHERE
        pid IN (
            SELECT
                id
            FROM
                lp_sys_menu
            WHERE
                pid IN (
                    SELECT
                        id
                    FROM
                        lp_sys_menu
                    WHERE
                        pid = '-1'
                )
        )
    UNION
    SELECT
        id
    FROM
        lp_sys_menu
    WHERE
        pid IN (
            SELECT
                id
            FROM
                lp_sys_menu
            WHERE
                pid IN (
                    SELECT
                        id
                    FROM
                        lp_sys_menu
                    WHERE
                        pid IN (
                            SELECT
                                id
                            FROM
                                lp_sys_menu
                            WHERE
                                pid = '-1'
                        )
                )
        )

     

     

      后记

      树形结构工具类暂时先记录到这,后续再进行补充