package com.ruoyi.common.utils; import com.ruoyi.common.core.domain.entity.SysDept; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; /** * 树结构工具类:根据 parentId 构造树、一次性获取所有叶子节点。 * 核心方法为泛型实现,id / parentId / children 通过函数注入,可复用于任何节点类型。 */ public class TreeUtils { private TreeUtils() { } /* ================== 通用方法 ================== */ /** * 根据 parentId 构造树,O(n) 一次遍历。 * 根节点为「父Id不在集合中」的节点;若不存在根节点(如全是自引用的环)则原样返回。 * * @param nodes 节点列表 * @param idGetter 取节点id * @param parentIdGetter 取父节点id * @param childrenSetter 设置子节点列表 */ public static List buildTree( List nodes, Function idGetter, Function parentIdGetter, BiConsumer> childrenSetter) { if (nodes == null || nodes.isEmpty()) { return new ArrayList<>(); } // 按父Id分组,一次遍历拿到每个节点的子列表 Map> byParent = nodes.stream().collect(Collectors.groupingBy(parentIdGetter)); for (T node : nodes) { childrenSetter.accept(node, byParent.getOrDefault(idGetter.apply(node), new ArrayList<>())); } Set ids = nodes.stream().map(idGetter).filter(Objects::nonNull).collect(Collectors.toSet()); List roots = nodes.stream() .filter(n -> !ids.contains(parentIdGetter.apply(n))) .collect(Collectors.toList()); return roots.isEmpty() ? nodes : roots; } /** * 一次性获取所有叶子节点:即「id 不出现在任何节点的 parentId 中」的节点,O(n) 一次遍历。 * * @param nodes 节点列表 * @param idGetter 取节点id * @param parentIdGetter 取父节点id */ public static List getLeafNodes( List nodes, Function idGetter, Function parentIdGetter) { if (nodes == null || nodes.isEmpty()) { return new ArrayList<>(); } Set parentIds = nodes.stream() .map(parentIdGetter) .filter(Objects::nonNull) .collect(Collectors.toSet()); return nodes.stream() .filter(n -> !parentIds.contains(idGetter.apply(n))) .collect(Collectors.toList()); } /* ================== SysDept 便捷方法 ================== */ /** * 根据 SysDept.parentId 构造部门树 */ public static List buildDeptTree(List depts) { return buildTree(depts, SysDept::getDeptId, SysDept::getParentId, SysDept::setChildren); } /** * 一次性获取所有部门叶子节点 */ public static List getLeafDepts(List depts) { return getLeafNodes(depts, SysDept::getHisDeptId, SysDept::getHisParentId); } }