作用
用于快速将带有树形结构的数据列表转为树结构,注意事项:
- 节点必须有
id字段,用于唯一标识 - 节点必须有
parentId字段,用于标识父节点 - 节点必须有
sort字段,用于排序 (数值越大越靠前)
代码
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);