Java 企业级工具类全家桶-44-树形结构构建工具类

📌 拿来即用 : Java 企业级工具类全家桶-44-树形结构构建工具类

📋 目录


一、工具类功能

把带父子关系的平铺列表一次组装成多级树,菜单、部门、地区、评论盖楼都走同一套入口,另外支持按主键在树里找节点、把树摊回前序列表。

典型用法是小表全量加载后组装成树直接返给前端,拿回来的是根节点列表、Optional 节点与摊平后的列表。类本身零第三方依赖,节点类只要有可读写的子集合字段(getter + setter)即可。


二、所需环境

JDK 17或以上


三、方法概览(按需调用)

序号 方法 入参 返回 用途
1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter) 平铺列表 + 主键取法 + 父键取法 + 子集合取法 + 子集合写法 List 自动判定根节点,一次组装多级树
2 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter, rootParentId) 平铺列表 + 主键取法 + 父键取法 + 子集合取法 + 子集合写法 + 根节点父键 List 显式指定根节点的父键(如菜单表的 0)
3 findById(roots, targetId, idGetter, childrenGetter) 根节点列表 + 目标主键 + 主键取法 + 子集合取法 Optional 在树里按主键找节点,未命中给空 Optional
4 flatten(roots, childrenGetter) 根节点列表 + 子集合取法 List 把树摊回前序列表,父节点在子节点之前

四、调用示例(直观体验)

调用示例直接复制进 IDE 右键 Run 就能看到调用工具类各方法的输出结果。

java 复制代码
21:00:31.858 [main] INFO -- 「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」返回根节点列表 -> [总部(1)[研发部(2)[服务端组(4), 前端组(5)], 市场部(3)[华东区(6)]]]
21:00:31.862 [main] INFO -- 「TreeBuildUtils 方法2 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter, rootParentId)」根父键给 0 时的根节点 -> [总部(1)[研发部(2)[服务端组(4), 前端组(5)], 市场部(3)[华东区(6)]]]
21:00:31.863 [main] INFO -- 「TreeBuildUtils 方法3 findById(roots, targetId, idGetter, childrenGetter)」命中节点 -> Optional[服务端组(4)]
21:00:31.863 [main] INFO -- 「TreeBuildUtils 方法3 findById(roots, targetId, idGetter, childrenGetter)」主键不存在 -> Optional.empty
21:00:31.865 [main] INFO -- 「TreeBuildUtils 方法4 flatten(roots, childrenGetter)」前序摊平的id序列 -> [1, 2, 4, 5, 3, 6]
21:00:31.866 [main] INFO -- 「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」父缺失时提升为根 -> [孤立菜单(10)[孤立的子菜单(11)]]
21:00:31.867 [main] INFO -- 「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」互相引用时裁一条边 -> [20],摊平后 -> [20, 21]
21:00:31.867 [main] ERROR -- 「异常信息获取演示」主键取法为null -> idGetter 不能为 null
21:00:31.868 [main] ERROR -- 「异常信息获取演示」子集合写法为null -> childrenSetter 不能为 null
21:00:31.868 [main] ERROR -- 「异常信息获取演示」子集合取法为null -> childrenGetter 不能为 null

进程已结束,退出代码为 0
java 复制代码
package com.example.order.api;

import com.example.common.utils.TreeBuildUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * 调用示例:整个文件复制进 IDE,右键 Run 就能看效果。
 *
 * <p>建树工具只吃内存里的平铺列表,不依赖外部服务,也不需要 Spring 容器;节点类只要有可读写的
 * 子集合字段(getter + setter)就能用,示例把菜单表组装成三级树,再演示查找、摊平与脏数据口径。
 * 日志用 Lombok 的 {@code @Slf4j} + SLF4J 占位符,没装 Lombok 就把 {@code log} 换成项目自己的 Logger。
 */
@Slf4j
public class TreeBuildUtilsDemo {

    /** 示例用的菜单节点,实际换成你自己的实体 */
    public static class Menu {

        private final Long id;

        private final Long parentId;

        private final String name;

        private List<Menu> children = new ArrayList<>();

        public Menu(Long id, Long parentId, String name) {
            this.id = id;
            this.parentId = parentId;
            this.name = name;
        }

        public Long getId() {
            return id;
        }

        public Long getParentId() {
            return parentId;
        }

        public String getName() {
            return name;
        }

        public List<Menu> getChildren() {
            return children;
        }

        public void setChildren(List<Menu> children) {
            this.children = children;
        }

        /** 打成「名称(主键)[子节点...]」,树的结构在日志里一眼可读 */
        @Override
        public String toString() {
            return children.isEmpty() ? name + "(" + id + ")" : name + "(" + id + ")" + children;
        }
    }

    public static void main(String[] args) {

        // ========== 一、平铺列表自动判定根建树 ==========

        List<Menu> roots = TreeBuildUtils.buildTree(menus(), Menu::getId, Menu::getParentId,
                Menu::getChildren, Menu::setChildren);
        log.info("「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」返回根节点列表 -> {}", roots);

        // ========== 二、显式指定根父键 ==========

        List<Menu> menuRoots = TreeBuildUtils.buildTree(menus(), Menu::getId, Menu::getParentId,
                Menu::getChildren, Menu::setChildren, 0L);
        log.info("「TreeBuildUtils 方法2 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter, rootParentId)」根父键给 0 时的根节点 -> {}",
                menuRoots);

        // ========== 三、树里按主键查找与摊平 ==========

        Optional<Menu> hit = TreeBuildUtils.findById(roots, 4L, Menu::getId, Menu::getChildren);
        log.info("「TreeBuildUtils 方法3 findById(roots, targetId, idGetter, childrenGetter)」命中节点 -> {}", hit);

        Optional<Menu> miss = TreeBuildUtils.findById(roots, 99L, Menu::getId, Menu::getChildren);
        log.info("「TreeBuildUtils 方法3 findById(roots, targetId, idGetter, childrenGetter)」主键不存在 -> {}", miss);

        List<Menu> flattened = TreeBuildUtils.flatten(roots, Menu::getChildren);
        log.info("「TreeBuildUtils 方法4 flatten(roots, childrenGetter)」前序摊平的id序列 -> {}", ids(flattened));

        // ========== 四、脏数据与环的处理 ==========

        // 父键 999 不在本批数据里:节点不丢,整条被提升为根
        List<Menu> orphan = new ArrayList<>(List.of(new Menu(10L, 999L, "孤立菜单")));
        orphan.add(new Menu(11L, 10L, "孤立的子菜单"));
        List<Menu> orphanRoots = TreeBuildUtils.buildTree(orphan, Menu::getId, Menu::getParentId,
                Menu::getChildren, Menu::setChildren);
        log.info("「TreeBuildUtils 方法1 buildTree(list, idGetter, parentIdGetter, childrenGetter, childrenSetter)」父缺失时提升为根 -> {}", orphanRoots);

        // 两条记录互相引用:环上只裁一条父边,节点一个不少
        List<Menu> cycle = new ArrayList<>(Arrays.asList(new Menu(20L, 21L, "环上A"),
                new Menu(21L, 20L, "环上B")));
        List<Menu> cycleRoots = TreeBuildUtils.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 (NullPointerException e) {
            log.error("「异常信息获取演示」主键取法为null -> {}", e.getMessage());
        }

        // 2. 子集合写法没给
        try {
            TreeBuildUtils.buildTree(menus(), Menu::getId, Menu::getParentId, Menu::getChildren, null);
        } catch (NullPointerException e) {
            log.error("「异常信息获取演示」子集合写法为null -> {}", e.getMessage());
        }

        // 3. 摊平时没给子集合取法
        try {
            TreeBuildUtils.flatten(roots, null);
        } catch (NullPointerException e) {
            log.error("「异常信息获取演示」子集合取法为null -> {}", e.getMessage());
        }
    }

    /** 平铺的菜单表:根节点的父键是 0,同级顺序就是这里的书写顺序 */
    private static List<Menu> menus() {
        return new ArrayList<>(List.of(
                new Menu(1L, 0L, "总部"),
                new Menu(2L, 1L, "研发部"),
                new Menu(3L, 1L, "市场部"),
                new Menu(4L, 2L, "服务端组"),
                new Menu(5L, 2L, "前端组"),
                new Menu(6L, 3L, "华东区")));
    }

    private static List<Long> ids(List<Menu> nodes) {
        return nodes.stream().map(Menu::getId).toList();
    }
}

五、工具类源码(可直接复制)

📦 「点击此处可下载 Java企业级工具类全家桶-44-树形结构构建工具类」 的完整源码,拿来即用。


觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯

相关推荐
AI人工智能+电脑小能手1 天前
Java 企业级工具类全家桶-48-分组聚合工具类
stream·jdk 17·java工具类·map集合·空值安全·分组聚合·计数求和
AI人工智能+电脑小能手1 天前
Java 企业级工具类全家桶-50-集合排序工具类
comparator·jdk 17·拼音排序·集合排序·java工具类·空值安全·多字段排序
AI人工智能+电脑小能手2 天前
Java 企业级工具类全家桶-74-JSON序列化工具类
spring boot·jackson·jdk 17·objectmapper·java工具类·json序列化·dto互转
AI人工智能+电脑小能手3 天前
Java 企业级工具类全家桶-68-HTTP请求工具类
spring boot·网络请求·http请求·resttemplate·jdk 17·java工具类·企业级开发
cpp_25014 个月前
P11375 [GESP202412 六级] 树上游走
数据结构·c++·算法·题解·洛谷·树形结构·gesp六级
cpp_25014 个月前
P10722 [GESP202406 六级] 二叉树
数据结构·c++·算法·题解·洛谷·树形结构·gesp六级
【JAVA】玩家5 个月前
Oracle树形查询实战:CONNECT BY双向遍历
达梦·树形结构·connect by·层级查询
虚诚9 个月前
vue2中树形表格怎么实现
前端·javascript·vue.js·ecmascript·vue2·树形结构
EndingCoder1 年前
算法与前端的可访问性
前端·算法·递归·树形结构