列表转树结构工具类 使用泛型支持任意类型的树形结构转换

/**
 * 列表转树结构工具类
 * 使用泛型支持任意类型的树形结构转换
 *
 * @param <T>  节点类型
 * @param <ID> 节点ID类型
 * @Author: liftSail
 * @Date: 2026/6/30 17:50
 * @Description: 不积跬步无以至千里
 */
public class TreeUtils<T, ID> {

    /**
     * 将列表转换为树结构
     *
     * @param list        原始列表
     * @param getId       获取节点ID的函数
     * @param getParentId 获取父节点ID的函数
     * @param setChildren 设置子节点列表的函数
     * @return 树形结构列表(顶级节点,parentId为0)
     */
    public static <T, ID> List<T> buildTree(List<T> list, Function<T, ID> getId, Function<T, ID> getParentId, BiConsumer<T, List<T>> setChildren) {
        if (list == null || list.isEmpty()) {
            return new ArrayList<>();
        }

        List<T> rootNodes = new ArrayList<>();

        // 找到所有顶级节点(parentId为0)
        for (T node : list) {
            ID parentId = getParentId.apply(node);
            // parentId为0表示顶级节点
            if (parentId == null || parentId.equals(0) || parentId.equals(0L) || NumericEnums.ZERO.getValue().equals(parentId)) {
                rootNodes.add(node);
            }
        }

        // 为每个顶级节点递归查找子节点
        for (T root : rootNodes) {
            buildChildren(root, list, getId, getParentId, setChildren);
        }

        return rootNodes;
    }

    /**
     * 递归构建子节点
     *
     * @param parent      父节点
     * @param list        原始列表
     * @param getId       获取节点ID的函数
     * @param getParentId 获取父节点ID的函数
     * @param setChildren 设置子节点列表的函数
     */
    private static <T, ID> void buildChildren(T parent, List<T> list, Function<T, ID> getId, Function<T, ID> getParentId, BiConsumer<T, List<T>> setChildren) {
        ID parentId = getId.apply(parent);
        List<T> children = new ArrayList<>();

        // 查找所有子节点
        for (T node : list) {
            ID nodeParentId = getParentId.apply(node);
            if (parentId.equals(nodeParentId)) {
                children.add(node);
                // 递归构建子节点的子节点
                buildChildren(node, list, getId, getParentId, setChildren);
            }
        }

        // 设置子节点列表
        setChildren.accept(parent, children);
    }

    /**
     * 将列表转换为树结构(使用默认的属性名称:id, parentId, children)
     * 适用于具有标准树形结构属性的对象
     *
     * @param list              原始列表
     * @param idFieldName       节点ID属性名称
     * @param parentIdFieldName 父节点ID属性名称
     * @param childrenFieldName 子节点列表属性名称
     * @return 树形结构列表
     */
    public static <T> List<T> buildTreeWithDefaultProps(List<T> list, String idFieldName, String parentIdFieldName, String childrenFieldName) {
        return buildTree(list, node -> (Long) getProperty(node, idFieldName), node -> {
            Object parentId = getProperty(node, parentIdFieldName);
            return parentId == null ? 0L : (Long) parentId;
        }, (node, children) -> {
            try {
                java.lang.reflect.Field field = node.getClass().getDeclaredField(childrenFieldName);
                field.setAccessible(true);
                field.set(node, children);
            } catch (Exception e) {
                throw new RuntimeException("Failed to get children field", e);
            }
        });
    }

    /**
     * 通过反射获取对象属性值
     */
    private static Object getProperty(Object obj, String fieldName) {
        try {
            java.lang.reflect.Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            throw new RuntimeException("Failed to get property: " + fieldName, e);
        }
    }
}

 

posted @ 2026-07-11 11:41  liftsail  阅读(2)  评论(0)    收藏  举报