树工具类-TreeUtil
# 📚简介
TreeUtil是Hutool树结构模块的核心工具类,提供了一套完整的树结构构建、遍历、查询解决方案。它通过简洁的API,帮助开发者快速构建和操作各类树状结构,如组织架构树、菜单树、分类树等。
# 🌲核心功能
- 树结构构建:支持从集合、Map、实体类等多种数据源构建树结构
- 单根节点构建:支持构建只有一个根节点的树结构
- 节点查询:支持根据ID查询节点、获取父节点链等
- 树遍历:支持深度优先遍历和广度优先遍历
- 灵活配置:支持自定义节点ID、父ID、名称、权重等字段
- 排序功能:支持按照权重或其他字段排序
# 🎯使用场景
- 组织架构管理:构建公司部门层级结构,支持查询部门上下级关系
- 菜单管理:构建系统菜单树,支持多级菜单展示
- 分类管理:构建商品分类树、文章分类树等
- 权限管理:构建权限树,实现基于角色的权限控制
- 地区管理:构建省市区层级结构,支持地址选择
# 🚀快速开始
# 树结构构建
// 定义树节点实体
class Dept implements TreeNode<Integer> {
private Integer id;
private Integer parentId;
private String name;
// getter和setter方法
// ...
@Override
public Integer getId() {
return id;
}
@Override
public Integer getParentId() {
return parentId;
}
@Override
public String getName() {
return name;
}
}
// 构建树结构
List<Dept> deptList = new ArrayList<>();
deptList.add(new Dept(1, 0, "技术中心"));
deptList.add(new Dept(2, 1, "研发部"));
deptList.add(new Dept(3, 1, "测试部"));
deptList.add(new Dept(4, 2, "Java组"));
deptList.add(new Dept(5, 2, "前端组"));
// 构建树
List<MapTree<Integer>> tree = TreeUtil.build(deptList, 0, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
});
# 单根节点构建
// 构建单根节点树
MapTree<Integer> singleTree = TreeUtil.buildSingle(deptList, 0, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
});
# 节点查询
// 根据ID查询节点
MapTree<Integer> node = TreeUtil.getNode(singleTree, 4);
// 获取所有父节点名称
List<CharSequence> parentNames = TreeUtil.getParentsName(node, true);
// 输出:[Java组, 研发部, 技术中心]
# 树遍历
// 转为列表(深度优先)
List<MapTree<Integer>> list1 = TreeUtil.toList(singleTree, false);
// 转为列表(广度优先)
List<MapTree<Integer>> list2 = TreeUtil.toList(singleTree, true);
# 📖方法详解
# buildSingle
功能:构建单root节点树,默认以0为根节点ID
参数:
list:源数据集合
返回值:MapTree<Integer> - 单根节点树
使用示例:
MapTree<Integer> tree = TreeUtil.buildSingle(deptList);
# build
功能:构建树结构,默认以0为根节点父ID
参数:
list:源数据集合
返回值:List<MapTree<Integer>> - 树节点列表
使用示例:
List<MapTree<Integer>> tree = TreeUtil.build(deptList);
# buildSingle(list, parentId)
功能:构建单root节点树,指定根节点父ID
参数:
list:源数据集合parentId:最顶层父ID值,一般为0
返回值:MapTree<E> - 单根节点树
使用示例:
MapTree<Integer> tree = TreeUtil.buildSingle(deptList, 0);
# build(list, parentId)
功能:构建树结构,指定根节点父ID
参数:
list:源数据集合parentId:最顶层父ID值,一般为0
返回值:List<MapTree<E>> - 树节点列表
使用示例:
List<MapTree<Integer>> tree = TreeUtil.build(deptList, 0);
# buildSingle(list, parentId, nodeParser)
功能:构建单root节点树,使用自定义节点解析器
参数:
list:源数据集合parentId:最顶层父ID值nodeParser:节点解析器,用于将自定义对象转换为树节点
返回值:MapTree<E> - 单根节点树
使用示例:
MapTree<Integer> tree = TreeUtil.buildSingle(deptList, 0, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
});
# build(list, parentId, nodeParser)
功能:构建树结构,使用自定义节点解析器
参数:
list:源数据集合parentId:最顶层父ID值nodeParser:节点解析器
返回值:List<MapTree<E>> - 树节点列表
使用示例:
List<MapTree<Integer>> tree = TreeUtil.build(deptList, 0, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
});
# build(list, rootId, treeNodeConfig, nodeParser)
功能:构建树结构,使用自定义配置和节点解析器
参数:
list:源数据集合rootId:最顶层父ID值treeNodeConfig:节点配置,用于自定义节点字段映射nodeParser:节点解析器
返回值:List<MapTree<E>> - 树节点列表
使用示例:
// 自定义节点配置
TreeNodeConfig config = new TreeNodeConfig();
config.setIdKey("deptId");
config.setParentIdKey("parentDeptId");
List<MapTree<Integer>> tree = TreeUtil.build(deptList, 0, config, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
});
# buildSingle(list, rootId, treeNodeConfig, nodeParser)
功能:构建单root节点树,使用自定义配置和节点解析器
参数:
list:源数据集合rootId:最顶层父ID值treeNodeConfig:节点配置nodeParser:节点解析器
返回值:MapTree<E> - 单根节点树
使用示例:
MapTree<Integer> tree = TreeUtil.buildSingle(deptList, 0, config, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
});
# getNode(node, id)
功能:根据ID查询节点,采用递归深度优先遍历
参数:
node:起始节点id:要查询的节点ID
返回值:MapTree<T> - 查询到的节点,未找到返回null
使用示例:
MapTree<Integer> node = TreeUtil.getNode(tree, 4);
# getParentsName(node, includeCurrentNode)
功能:获取所有父节点名称列表
参数:
node:起始节点includeCurrentNode:是否包含当前节点名称
返回值:List<CharSequence> - 父节点名称列表
使用示例:
List<CharSequence> parentNames = TreeUtil.getParentsName(node, true);
# getParentsId(node, includeCurrentNode)
功能:获取所有父节点ID列表
参数:
node:起始节点includeCurrentNode:是否包含当前节点ID
返回值:List<T> - 父节点ID列表
使用示例:
List<Integer> parentIds = TreeUtil.getParentsId(node, true);
# getParents(node, includeCurrentNode, fieldFunc)
功能:获取所有父节点指定字段值列表
参数:
node:起始节点includeCurrentNode:是否包含当前节点fieldFunc:字段获取函数
返回值:List<E> - 父节点字段值列表
使用示例:
List<Integer> parentIds = TreeUtil.getParents(node, true, MapTree::getId);
# createEmptyNode(id)
功能:创建空的树节点
参数:
id:节点ID
返回值:MapTree<E> - 空树节点
使用示例:
MapTree<Integer> emptyNode = TreeUtil.createEmptyNode(0);
# toList(root, broadFirst)
功能:将树转换为列表,支持深度优先和广度优先遍历
参数:
root:根节点broadFirst:是否广度优先遍历,false为深度优先
返回值:List<MapTree<E>> - 树节点列表
使用示例:
// 深度优先遍历
List<MapTree<Integer>> list1 = TreeUtil.toList(tree, false);
// 广度优先遍历
List<MapTree<Integer>> list2 = TreeUtil.toList(tree, true);
# 💡注意事项
- 节点ID唯一性:构建树时,所有节点的ID必须唯一
- 根节点父ID:构建树时,指定的rootId应该是首层节点的parentId,而非某个节点的id
- 空节点处理:当数据源为空时,build方法会返回空列表
- 遍历性能:对于大型树结构,遍历操作可能影响性能,建议合理使用
- 线程安全:TreeUtil类的方法不是线程安全的,多线程环境下需要加锁保护
# 📦相关类
- MapTree:基于Map实现的树节点,支持动态扩展属性
- TreeNode:树节点接口,定义了树节点的基本属性和行为
- TreeNodeConfig:树节点配置类,用于自定义节点字段映射
- TreeBuilder:树构建器,提供流式API构建树结构
- NodeParser:节点解析器接口,用于将自定义对象转换为树节点
# 🎨扩展使用
# 自定义节点配置
// 自定义节点配置
TreeNodeConfig config = new TreeNodeConfig();
config.setIdKey("id");
config.setParentIdKey("pid");
config.setNameKey("name");
config.setWeightKey("sort");
config.setChildrenKey("kids");
// 使用自定义配置构建树
List<MapTree<Integer>> tree = TreeUtil.build(deptList, 0, config, (dept, treeNode) -> {
treeNode.setId(dept.getId());
treeNode.setParentId(dept.getParentId());
treeNode.setName(dept.getName());
treeNode.setWeight(dept.getSort());
});
# 从Map构建树
// 构建节点Map
Map<Integer, MapTree<Integer>> nodeMap = new HashMap<>();
MapTree<Integer> root = new MapTree<>();
root.setId(0);
root.setName("根节点");
nodeMap.put(0, root);
MapTree<Integer> child1 = new MapTree<>();
child1.setId(1);
child1.setParentId(0);
child1.setName("子节点1");
nodeMap.put(1, child1);
// 从Map构建树
List<MapTree<Integer>> tree = TreeUtil.build(nodeMap, 0);
TreeUtil类提供了丰富的树操作API,能够满足大部分树结构处理需求。通过灵活使用这些方法,可以快速构建和操作各类树状结构,提高开发效率。