/**
* 列表转树结构工具类
* 使用泛型支持任意类型的树形结构转换
*
* @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);
}
}
}