logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔4
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置
前端编程3
前端基础知识16
UniApp15
Vue框架19
Java编程7
Java基础知识14
SpringBoot31
SpringMVC14
MyBatis8
SpringCloud15
RocketMQ1
MyCat1
数据库4
MySQL6
Redis7
MongoDB10
H21
Java技术栈4
知识整理分布式锁的实现方案MySQL事务MySQL的锁
软件工具箱14
IDEAGitMavenGradleNginx安装Nginx配置JMeter压测OllamaPicGoRustFSVSCodeDockerObsidianObs录制
Linux知识11
树莓派安装及使用ArchLinux:常用软件ArchLinux:基础系统安装ArchLinux:深度优化ArchLInux:图形化界面安装ArchLinux:Nirifrp内网穿透Jar启动脚本Linux常用命令VirtualBox安装CentOSVirtualBox安装Ubuntu
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

作用

用于快速将带有树形结构的数据列表转为树结构,注意事项:

代码

package com.fan.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 树形数据处理工具类
 */
public class TreeDataUtil {

    private static final Logger logger = LoggerFactory.getLogger(TreeDataUtil.class);

    /**
     * 通用树形数据处理
     * 列表数据需要满足以下条件:
     * 1. 节点必须有 id 字段,用于唯一标识
     * 2. 节点必须有 parentId 字段,用于标识父节点
     * 3. 节点必须有 sort 字段,用于排序 (数值越大越靠前)
     *
     * @param allNodes 需要处理的平铺数据列表
     * @return 构建树形结构后的根节点列表 (List<T>)
     */
    public <T> List<T> listToTree(List<T> allNodes) {
        if (allNodes == null || allNodes.isEmpty()) {
            return new ArrayList<>();
        }

        // 1. 先进行排序:sort 字段降序排列 (越大越靠前)
        sortNodes(allNodes);

        // 2. 构建树形结构
        return buildTree(allNodes);
    }

    /**
     * 对节点列表进行排序
     */
    private <T> void sortNodes(List<T> allNodes) {
        try {
            // 检查是否包含 sort 字段,如果包含则排序
            allNodes.get(0).getClass().getDeclaredField("sort");

            // 执行排序
            allNodes.sort((o1, o2) -> {
                Object sort1 = getFieldValue(o1, "sort");
                Object sort2 = getFieldValue(o2, "sort");

                // 处理 null 值,默认视为 0
                Number n1 = sort1 == null ? 0 : (Number) sort1;
                Number n2 = sort2 == null ? 0 : (Number) sort2;

                // 降序:o2 - o1
                return Double.compare(n2.doubleValue(), n1.doubleValue());
            });
        } catch (NoSuchFieldException e) {
            // 如果实体类没有 sort 字段,则不进行排序,直接跳过
            logger.debug("Node class does not contain 'sort' field, skipping sorting.");
        } catch (Exception e) {
            logger.warn("Sorting failed: {}", e.getMessage());
        }
    }

    /**
     * 核心递归构建树的方法
     */
    private <T> List<T> buildTree(List<T> allNodes) {
        List<T> rootNodes = new ArrayList<>();

        // 使用 Map 存储 ID 到对象的映射,提高查找效率 O(1)
        Map<Object, T> nodeMap = new HashMap<>();
        for (T node : allNodes) {
            Object id = getFieldValue(node, "id");
            if (id != null) {
                nodeMap.put(id, node);
            }
        }

        // 遍历所有节点,将节点添加到其父节点的 children 列表中
        for (T node : allNodes) {
            Object parentId = getFieldValue(node, "parentId");

            // 判断是否为根节点
            if (isRoot(parentId)) {
                rootNodes.add(node);
            } else {
                // 尝试找到父节点
                T parent = nodeMap.get(parentId);
                if (parent != null) {
                    addChild(parent, node);
                }
                // 如果找不到父节点,视为上级已经被删除,下级不再展示
            }
        }

        return rootNodes;
    }

    /**
     * 判断是否为根节点
     */
    private boolean isRoot(Object parentId) {
        if (parentId == null) {
            return true;
        }
        String val = String.valueOf(parentId);
        return "0".equals(val) || "".equals(val);
    }

    /**
     * 利用反射获取字段值
     */
    private Object getFieldValue(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            // 尝试通过 Getter 方法获取
            try {
                String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                Method method = obj.getClass().getMethod(getterName);
                return method.invoke(obj);
            } catch (Exception ex) {
                // 静默处理,返回 null
                return null;
            }
        }
    }

    /**
     * 利用反射将子节点添加到父节点的 children 列表中
     */
    @SuppressWarnings("unchecked")
    private void addChild(Object parent, Object child) {
        try {
            Field childrenField = parent.getClass().getDeclaredField("children");
            childrenField.setAccessible(true);

            // 获取当前的 children 对象
            Object childrenObj = childrenField.get(parent);
            List<Object> children;

            // 安全检查:确保它是 List 类型
            if (childrenObj instanceof List) {
                // 这里利用泛型擦除,将 List<?> 视为原始 List 进行操作
                // 虽然会有 "unchecked" 警告,但比直接强转 Object 到 List<Object> 更安全
                children = (List<Object>) childrenObj;
            } else {
                // 如果为 null,初始化一个新的 ArrayList
                children = new ArrayList<>();
                childrenField.set(parent, children);
            }

            children.add(child);

        } catch (NoSuchFieldException e) {
            logger.warn("Target class [{}] is missing 'children' field.", parent.getClass().getName());
        } catch (IllegalAccessException e) {
            logger.error("Failed to access children field in class [{}]", parent.getClass().getName(), e);
        }
    }
}

使用方式

// 树形数据处理
List<TbAddressListResp> listTree = new TreeDataUtil().listToTree(list);
作用
代码
使用方式