首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

树形结构工具类

编程知识
2024年09月25日 14:49

  前言

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

  封装了以下方法:

  根据父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'
                    )
            )
    )

 

 

  后记

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

 

From:https://www.cnblogs.com/huanzi-qch/p/18431505
本文地址: http://www.shuzixingkong.net/article/2298
0评论
提交 加载更多评论
其他文章 大模型训练:K8s 环境中数千节点存储最佳实践
今天这篇博客来自全栈工程师朱唯唯,她在前不久举办的 KubeCon 中国大会上进行了该主题分享。 Kubernetes 已经成为事实的应用编排标准,越来越多的应用在不断的向云原生靠拢。与此同时,人工智能技术的迅速发展,尤其是大型语言模型(LLM)的推进,导致企业需要处理的数据量急剧增加,例如,Lla
大模型训练:K8s 环境中数千节点存储最佳实践 大模型训练:K8s 环境中数千节点存储最佳实践 大模型训练:K8s 环境中数千节点存储最佳实践
Python计算傅里叶变换
本文介绍了离散傅里叶变换和快速傅里叶变换的基本原理及其对应的Python代码实现,并将计算结果与numpy所集成的fft函数进行对比。其实现在FFT计算的成熟工具已经有很多了,不论是CPU上scipy的fft模块还是GPU上的cufft动态链接库,都有非常好的性能。但还是得真正去了解计算背后的原理,
Python计算傅里叶变换 Python计算傅里叶变换
.net 到底行不行!2000 人在线的客服系统真实屏录演示(附技术详解) 📹
时常有朋友问我性能方面的问题,正好有一个真实客户,在线的访客数量达到了 2000 人。在争得客户同意后,我录了一个视频。升讯威在线客服系统可以在极低配置的服务器环境下,轻松应对这种情况,依然可以做到消息毫秒级送达,操作毫秒级响应。
.net 到底行不行!2000 人在线的客服系统真实屏录演示(附技术详解) 📹 .net 到底行不行!2000 人在线的客服系统真实屏录演示(附技术详解) 📹 .net 到底行不行!2000 人在线的客服系统真实屏录演示(附技术详解) 📹
SimpleAIAgent:使用免费的glm-4-flash即可开始构建简单的AI Agent应用
SimpleAIAgent是基于C# Semantic Kernel 与 WPF构建的一款AI Agent探索应用。主要用于使用国产大语言模型或开源大语言模型构建AI Agent应用的探索学习,希望能够帮助到感兴趣的朋友。 接下来我想分享一下我的AI Agent应用实践。 翻译文本并将文本存入文件
SimpleAIAgent:使用免费的glm-4-flash即可开始构建简单的AI Agent应用 SimpleAIAgent:使用免费的glm-4-flash即可开始构建简单的AI Agent应用 SimpleAIAgent:使用免费的glm-4-flash即可开始构建简单的AI Agent应用
裁员,这一次终于轮到了我
新产品发行失利,流水逐渐下滑,裁员成了公司不可避免的选择,看着身边朝夕相处的同事一个个离开,甚至还要亲自去跟团队内的同事谈离职,真的很不是滋味。好在这一切即将结束,我也要走了,公司给了足额的赔偿,我觉得挺好,于我而言,这是最好的结果。公司如果赚钱,即便不会多发给我,那我也是干劲十足,我会觉得我的工作
Rust字符串类型全解析
字符串是每种编程语言都绕不开的类型, 不过,在Rust中,你会看到远比其他语言更加丰富多样的字符串类型。 如下图: 为什么Rust中需要这么多种表示字符串的类型呢? 初学Rust时,可能无法理解为什么要这样设计?为什么要给使用字符串带来这么多不必要的复杂性? 其实,Rust中对于字符串的设计,优先考
Rust字符串类型全解析 Rust字符串类型全解析 Rust字符串类型全解析
面试官:谈谈你对 IoC 和 AOP 的理解!
本文摘录自笔者开源的 Java 学习&amp;面试指南(Github 收获146k star):JavaGuide 。 这篇文章会从下面从以下几个问题展开对 IoC &amp; AOP 的解释 什么是 IoC? IoC 解决了什么问题? IoC 和 DI 的区别? 什么是 AOP? AOP 解决了什
面试官:谈谈你对 IoC 和 AOP 的理解! 面试官:谈谈你对 IoC 和 AOP 的理解! 面试官:谈谈你对 IoC 和 AOP 的理解!
技术博文的净土,博客园
最近一年里,园子发布了多篇“求救信”,无论园子的置顶文章,还是公众号平台,都在进行着发文。 在7年之前,那时候我在读大学一年级,因为对于计算机的兴趣,后来从学长那里了解到技术博客这个东西,最初接触的是某DN,后来发现博客园干净整洁无广告,页面清爽,后来又发现博客园竟然可以自定义,于是又不断的折腾自己