
拿来即用 Java 企业级工具类全家桶-44-树形结构构建工具类 目录一、工具类功能二、所需环境三、方法概览按需调用四、调用示例直观体验五、工具类源码可直接复制一、工具类功能把带父子关系的平铺列表一次组装成多级树菜单、部门、地区、评论盖楼都走同一套入口另外支持按主键在树里找节点、把树摊回前序列表。典型用法是小表全量加载后组装成树直接返给前端拿回来的是根节点列表、Optional 节点与摊平后的列表。类本身零第三方依赖节点类只要有可读写的子集合字段getter setter即可。二、所需环境JDK 17或以上三、方法概览按需调用序号方法入参返回用途1buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)平铺列表 主键取法 父键取法 子集合取法 子集合写法List自动判定根节点一次组装多级树2buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter, rootParentId)平铺列表 主键取法 父键取法 子集合取法 子集合写法 根节点父键List显式指定根节点的父键如菜单表的 03findById(roots, targetId, idGetter, childrenGetter)根节点列表 目标主键 主键取法 子集合取法Optional在树里按主键找节点未命中给空 Optional4flatten(roots, childrenGetter)根节点列表 子集合取法List把树摊回前序列表父节点在子节点之前四、调用示例直观体验调用示例直接复制进 IDE 右键 Run 就能看到调用工具类各方法的输出结果。21:00:31.858[main]INFO--「TreeBuildUtils方法1buildTree(list,idGetter,parentIdGetter,childrenGetter,childrenSetter)」返回根节点列表-[总部(1)[研发部(2)[服务端组(4),前端组(5)],市场部(3)[华东区(6)]]]21:00:31.862[main]INFO--「TreeBuildUtils方法2buildTree(list,idGetter,parentIdGetter,childrenGetter,childrenSetter,rootParentId)」根父键给0时的根节点-[总部(1)[研发部(2)[服务端组(4),前端组(5)],市场部(3)[华东区(6)]]]21:00:31.863[main]INFO--「TreeBuildUtils方法3findById(roots,targetId,idGetter,childrenGetter)」命中节点-Optional[服务端组(4)]21:00:31.863[main]INFO--「TreeBuildUtils方法3findById(roots,targetId,idGetter,childrenGetter)」主键不存在-Optional.empty21:00:31.865[main]INFO--「TreeBuildUtils方法4flatten(roots,childrenGetter)」前序摊平的id序列-[1,2,4,5,3,6]21:00:31.866[main]INFO--「TreeBuildUtils方法1buildTree(list,idGetter,parentIdGetter,childrenGetter,childrenSetter)」父缺失时提升为根-[孤立菜单(10)[孤立的子菜单(11)]]21:00:31.867[main]INFO--「TreeBuildUtils方法1buildTree(list,idGetter,parentIdGetter,childrenGetter,childrenSetter)」互相引用时裁一条边-[20]摊平后-[20,21]21:00:31.867[main]ERROR--「异常信息获取演示」主键取法为null-idGetter 不能为null21:00:31.868[main]ERROR--「异常信息获取演示」子集合写法为null-childrenSetter 不能为null21:00:31.868[main]ERROR--「异常信息获取演示」子集合取法为null-childrenGetter 不能为null进程已结束退出代码为0packagecom.example.order.api;importcom.example.common.utils.TreeBuildUtils;importlombok.extern.slf4j.Slf4j;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;importjava.util.Optional;/** * 调用示例整个文件复制进 IDE右键 Run 就能看效果。 * * p建树工具只吃内存里的平铺列表不依赖外部服务也不需要 Spring 容器节点类只要有可读写的 * 子集合字段getter setter就能用示例把菜单表组装成三级树再演示查找、摊平与脏数据口径。 * 日志用 Lombok 的 {code Slf4j} SLF4J 占位符没装 Lombok 就把 {code log} 换成项目自己的 Logger。 */Slf4jpublicclassTreeBuildUtilsDemo{/** 示例用的菜单节点实际换成你自己的实体 */publicstaticclassMenu{privatefinalLongid;privatefinalLongparentId;privatefinalStringname;privateListMenuchildrennewArrayList();publicMenu(Longid,LongparentId,Stringname){this.idid;this.parentIdparentId;this.namename;}publicLonggetId(){returnid;}publicLonggetParentId(){returnparentId;}publicStringgetName(){returnname;}publicListMenugetChildren(){returnchildren;}publicvoidsetChildren(ListMenuchildren){this.childrenchildren;}/** 打成「名称(主键)[子节点…]」树的结构在日志里一眼可读 */OverridepublicStringtoString(){returnchildren.isEmpty()?name(id):name(id)children;}}publicstaticvoidmain(String[]args){// 一、平铺列表自动判定根建树 ListMenurootsTreeBuildUtils.buildTree(menus(),Menu::getId,Menu::getParentId,Menu::getChildren,Menu::setChildren);log.info(「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」返回根节点列表 - {},roots);// 二、显式指定根父键 ListMenumenuRootsTreeBuildUtils.buildTree(menus(),Menu::getId,Menu::getParentId,Menu::getChildren,Menu::setChildren,0L);log.info(「TreeBuildUtils 方法2 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter, rootParentId)」根父键给 0 时的根节点 - {},menuRoots);// 三、树里按主键查找与摊平 OptionalMenuhitTreeBuildUtils.findById(roots,4L,Menu::getId,Menu::getChildren);log.info(「TreeBuildUtils 方法3 findById(roots, targetId, idGetter, childrenGetter)」命中节点 - {},hit);OptionalMenumissTreeBuildUtils.findById(roots,99L,Menu::getId,Menu::getChildren);log.info(「TreeBuildUtils 方法3 findById(roots, targetId, idGetter, childrenGetter)」主键不存在 - {},miss);ListMenuflattenedTreeBuildUtils.flatten(roots,Menu::getChildren);log.info(「TreeBuildUtils 方法4 flatten(roots, childrenGetter)」前序摊平的id序列 - {},ids(flattened));// 四、脏数据与环的处理 // 父键 999 不在本批数据里节点不丢整条被提升为根ListMenuorphannewArrayList(List.of(newMenu(10L,999L,孤立菜单)));orphan.add(newMenu(11L,10L,孤立的子菜单));ListMenuorphanRootsTreeBuildUtils.buildTree(orphan,Menu::getId,Menu::getParentId,Menu::getChildren,Menu::setChildren);log.info(「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」父缺失时提升为根 - {},orphanRoots);// 两条记录互相引用环上只裁一条父边节点一个不少ListMenucyclenewArrayList(Arrays.asList(newMenu(20L,21L,环上A),newMenu(21L,20L,环上B)));ListMenucycleRootsTreeBuildUtils.buildTree(cycle,Menu::getId,Menu::getParentId,Menu::getChildren,Menu::setChildren);log.info(「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」互相引用时裁一条边 - {}摊平后 - {},ids(cycleRoots),ids(TreeBuildUtils.flatten(cycleRoots,Menu::getChildren)));// 五、catch异常信息 // 1. 主键取法没给try{TreeBuildUtils.buildTree(menus(),null,Menu::getParentId,Menu::getChildren,Menu::setChildren);}catch(NullPointerExceptione){log.error(「异常信息获取演示」主键取法为null - {},e.getMessage());}// 2. 子集合写法没给try{TreeBuildUtils.buildTree(menus(),Menu::getId,Menu::getParentId,Menu::getChildren,null);}catch(NullPointerExceptione){log.error(「异常信息获取演示」子集合写法为null - {},e.getMessage());}// 3. 摊平时没给子集合取法try{TreeBuildUtils.flatten(roots,null);}catch(NullPointerExceptione){log.error(「异常信息获取演示」子集合取法为null - {},e.getMessage());}}/** 平铺的菜单表根节点的父键是 0同级顺序就是这里的书写顺序 */privatestaticListMenumenus(){returnnewArrayList(List.of(newMenu(1L,0L,总部),newMenu(2L,1L,研发部),newMenu(3L,1L,市场部),newMenu(4L,2L,服务端组),newMenu(5L,2L,前端组),newMenu(6L,3L,华东区)));}privatestaticListLongids(ListMenunodes){returnnodes.stream().map(Menu::getId).toList();}}五、工具类源码可直接复制 「点击此处可下载 Java企业级工具类全家桶-44-树形结构构建工具类」 的完整源码拿来即用。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~