万能树工具类 - 提供树形结构构建和排序功能
| 9 | * 万能树工具类 - 提供树形结构构建和排序功能 |
| 10 | */ |
| 11 | public class TreeUtil { |
| 12 | |
| 13 | /** |
| 14 | * 构建树形结构(核心方法) |
| 15 | * |
| 16 | * @param list 原始数据列表 |
| 17 | * @param idGetter 节点ID获取函数 |
| 18 | * @param parentIdGetter 父节点ID获取函数 |
| 19 | * @param childrenSetter 子节点设置函数 |
| 20 | * @param comparator 节点排序比较器(可为null) |
| 21 | * @param rootValues 根节点标识值集合(可为null) |
| 22 | * @param <T> 节点数据类型 |
| 23 | * @param <R> ID数据类型 |
| 24 | * @return 构建完成的树形结构列表 |
| 25 | */ |
| 26 | public static <T, R> List<T> buildTree( |
| 27 | List<T> list, |
| 28 | Function<T, R> idGetter, |
| 29 | Function<T, R> parentIdGetter, |
| 30 | BiConsumer<T, List<T>> childrenSetter, |
| 31 | Comparator<T> comparator, |
| 32 | Set<R> rootValues) { |
| 33 | |
| 34 | // 空列表安全处理 |
| 35 | if (Objects.isNull(list) || list.isEmpty()) { |
| 36 | return Collections.emptyList(); |
| 37 | } |
| 38 | |
| 39 | // 创建ID到节点的映射,用于快速查找节点 |
| 40 | Map<R, T> idMap = list.stream() |
| 41 | .collect(Collectors.toMap(idGetter, node -> node, (a, b) -> a)); |
| 42 | |
| 43 | // 处理根节点标识值(防止空指针) |
| 44 | Set<R> effectiveRootValues = Objects.nonNull(rootValues) ? rootValues : Collections.emptySet(); |
| 45 | |
| 46 | // 构建父ID到子节点列表的映射 |
| 47 | Map<R, List<T>> parentMap = new HashMap<>(); |
| 48 | list.forEach(node -> { |
| 49 | R parentId = parentIdGetter.apply(node); |
| 50 | // 使用computeIfAbsent确保每个父ID都有对应的列表 |
| 51 | parentMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(node); |
| 52 | }); |
| 53 | |
| 54 | // 检测循环引用(防止无限递归) |
| 55 | detectCycle(parentMap, idGetter, idMap); |
| 56 | |
| 57 | // 存储最终结果(根节点列表) |
| 58 | List<T> result = new ArrayList<>(); |
| 59 | |
| 60 | // 遍历所有节点构建树结构 |
| 61 | list.forEach(node -> { |
| 62 | R id = idGetter.apply(node); |
| 63 | // 获取当前节点的子节点列表 |
| 64 | List<T> children = parentMap.getOrDefault(id, Collections.emptyList()); |
| 65 | |
| 66 | // 对子节点进行排序(如果提供了比较器) |
| 67 | if (!children.isEmpty() && Objects.nonNull(comparator)) { |
| 68 | children.sort(comparator); |
nothing calls this directly
no outgoing calls
no test coverage detected