树构建器-TreeBuilder
# 🎯 简介
TreeBuilder 是 Hutool 树结构模块中的构建器类,实现了 Builder 接口,用于构建 MapTree 结构。它提供了流畅的 API 来添加节点、配置树结构,并最终生成完整的树。
# 📋 核心特性
- ✅ 支持泛型ID,适应不同业务场景
- ✅ 提供静态工厂方法,创建便捷
- ✅ 支持链式调用,使用流畅
- ✅ 支持多种节点添加方式(Map、Iterable、Iterator)
- ✅ 支持节点转换器,灵活转换Bean为树节点
- ✅ 支持树剪枝,限制递归深度
- ✅ 支持构建单根树或树列表
- ✅ 实现了Builder模式,符合设计原则
- ✅ 支持重置复用,提高性能
# 🛠️ 类结构
typedef TreeBuilder<E>
├── 实现自 Builder<MapTree<E>>
├── 属性
│ ├── root: MapTree<E> // 根节点
│ ├── idTreeMap: Map<E, MapTree<E>> // ID到节点的映射
│ └── isBuild: boolean // 是否已构建
└── 方法
├── 静态工厂方法
│ ├── of(rootId): TreeBuilder<T> // 创建树构建器
│ └── of(rootId, config): TreeBuilder<T> // 创建带配置的树构建器
├── 构造方法
│ ├── TreeBuilder(rootId, config) // 根据根ID和配置创建
│ └── TreeBuilder(root) // 根据根节点创建
├── 根节点配置
│ ├── setId(id): TreeBuilder<E> // 设置根节点ID
│ ├── setParentId(parentId): TreeBuilder<E> // 设置根节点父ID
│ ├── setName(name): TreeBuilder<E> // 设置根节点名称
│ ├── setWeight(weight): TreeBuilder<E> // 设置根节点权重
│ └── putExtra(key, value): TreeBuilder<E> // 向根节点添加扩展属性
├── 节点添加
│ ├── append(map): TreeBuilder<E> // 添加节点Map
│ ├── append(list, nodeParser): TreeBuilder<E> // 添加Bean列表并转换
│ ├── append(trees): TreeBuilder<E> // 添加节点列表
│ └── append(iterator): TreeBuilder<E> // 添加节点迭代器
├── 构建方法
│ ├── build(): MapTree<E> // 构建单根树
│ └── buildList(): List<MapTree<E>> // 构建树列表
└── 其他方法
└── reset(): TreeBuilder<E> // 重置构建器,支持复用
# 🚀 快速开始
# 基本使用
// 创建TreeBuilder
TreeBuilder<Long> builder = TreeBuilder.of(0L);
// 添加节点
MapTree<Long> node1 = new MapTree<>().setId(1L).setParentId(0L).setName("节点1");
MapTree<Long> node2 = new MapTree<>().setId(2L).setParentId(0L).setName("节点2");
MapTree<Long> node3 = new MapTree<>().setId(3L).setParentId(1L).setName("节点3");
// 向构建器添加节点
builder.append(List.of(node1, node2, node3));
// 构建树
MapTree<Long> tree = builder.build();
// 输出树结构
Console.log("构建的树: {}", tree);
# 使用配置和节点转换器
// 创建配置
TreeNodeConfig config = new TreeNodeConfig();
config.setNameKey("title").setChildrenKey("items");
// 定义实体类
class Menu {
private Long id;
private Long parentId;
private String title;
// 构造方法、getter和setter省略
}
// 创建菜单列表
List<Menu> menuList = new ArrayList<>();
menuList.add(new Menu(1L, 0L, "菜单1"));
menuList.add(new Menu(2L, 0L, "菜单2"));
menuList.add(new Menu(3L, 1L, "子菜单1"));
// 创建TreeBuilder并添加节点
TreeBuilder<Long> builder = TreeBuilder.of(0L, config);
builder.append(menuList, (menu, node) -> {
node.setId(menu.getId())
.setParentId(menu.getParentId())
.setName(menu.getTitle());
});
// 构建树
MapTree<Long> tree = builder.build();
// 输出树结构
Console.log("菜单树: {}", tree);
# 📖 详细方法
# 静态工厂方法
# of(rootId)
功能:创建树构建器
参数:
rootId- 根节点ID返回值:
TreeBuilder<T>- 树构建器实例使用场景:快速创建树构建器,使用默认配置
示例:
TreeBuilder<Long> builder = TreeBuilder.of(0L);
# of(rootId, config)
功能:创建带配置的树构建器
参数:
rootId- 根节点IDconfig- 树节点配置
返回值:
TreeBuilder<T>- 树构建器实例使用场景:创建带自定义配置的树构建器
示例:
TreeNodeConfig config = new TreeNodeConfig(); config.setNameKey("label"); TreeBuilder<Long> builder = TreeBuilder.of(0L, config);
# 根节点配置方法
# setId(id)
功能:设置根节点ID
参数:
id- 根节点ID返回值:当前构建器实例,支持链式调用
使用场景:修改根节点ID
示例:
builder.setId(100L);
# setParentId(parentId)
功能:设置根节点父ID
参数:
parentId- 根节点父ID返回值:当前构建器实例,支持链式调用
使用场景:设置根节点的父ID,通常为null或特定值
示例:
builder.setParentId(null);
# setName(name)
功能:设置根节点名称
参数:
name- 根节点名称返回值:当前构建器实例,支持链式调用
使用场景:设置根节点的显示名称
示例:
builder.setName("根节点");
# setWeight(weight)
功能:设置根节点权重
参数:
weight- 根节点权重返回值:当前构建器实例,支持链式调用
使用场景:设置根节点的排序权重
示例:
builder.setWeight(1);
# putExtra(key, value)
功能:向根节点添加扩展属性
参数:
key- 属性键value- 属性值
返回值:当前构建器实例,支持链式调用
使用场景:向根节点添加自定义属性
示例:
builder.putExtra("icon", "🏠");
# 节点添加方法
# append(map)
功能:添加节点Map
参数:
map- ID到节点的映射返回值:当前构建器实例,支持链式调用
使用场景:批量添加已构建的节点
示例:
Map<Long, MapTree<Long>> nodeMap = new HashMap<>(); nodeMap.put(1L, new MapTree<Long>().setId(1L).setParentId(0L).setName("节点1")); builder.append(nodeMap);
# append(list, nodeParser)
功能:添加Bean列表并转换为节点
参数:
list- Bean列表nodeParser- 节点转换器,用于将Bean转换为节点
返回值:当前构建器实例,支持链式调用
使用场景:将数据库查询结果或其他Bean列表转换为树节点
示例:
builder.append(menuList, (menu, node) -> { node.setId(menu.getId()) .setParentId(menu.getParentId()) .setName(menu.getTitle()) .setWeight(menu.getSort()); });
# append(trees)
功能:添加节点列表
参数:
trees- 节点列表返回值:当前构建器实例,支持链式调用
使用场景:批量添加节点
示例:
List<MapTree<Long>> nodes = new ArrayList<>(); nodes.add(new MapTree<Long>().setId(1L).setParentId(0L).setName("节点1")); nodes.add(new MapTree<Long>().setId(2L).setParentId(0L).setName("节点2")); builder.append(nodes);
# append(iterator)
功能:添加节点迭代器
参数:
iterator- 节点迭代器返回值:当前构建器实例,支持链式调用
使用场景:通过迭代器添加节点,适用于大数据量场景
示例:
Iterator<MapTree<Long>> iterator = nodeList.iterator(); builder.append(iterator);
# 构建方法
# build()
功能:构建单根树
返回值:构建好的树的根节点
使用场景:构建有明确根节点的树结构
示例:
MapTree<Long> tree = builder.build();
# buildList()
功能:构建树列表,没有顶层节点
返回值:构建好的树列表
使用场景:构建多个并列的树结构
示例:
List<MapTree<Long>> treeList = builder.buildList();
# 其他方法
# reset()
功能:重置构建器,实现复用
返回值:当前构建器实例,支持链式调用
使用场景:重复使用同一个构建器构建多棵树
示例:
builder.reset(); // 重新添加节点并构建
# 🎨 使用场景
# 1. 构建菜单树
// 创建配置
TreeNodeConfig config = new TreeNodeConfig();
config.setNameKey("title").setChildrenKey("children").setWeightKey("sort");
// 从数据库获取菜单数据
List<Menu> menuList = menuService.findAll();
// 创建TreeBuilder
TreeBuilder<Long> builder = TreeBuilder.of(0L, config);
// 添加菜单节点
builder.append(menuList, (menu, node) -> {
node.setId(menu.getId())
.setParentId(menu.getParentId())
.setName(menu.getTitle())
.setWeight(menu.getSort())
.putExtra("icon", menu.getIcon())
.putExtra("url", menu.getUrl());
});
// 构建菜单树
MapTree<Long> menuTree = builder.build();
// 输出菜单树
Console.log("菜单树: {}", menuTree);
# 2. 构建部门树
// 创建TreeBuilder
TreeBuilder<Long> builder = TreeBuilder.of(0L);
// 部门数据
List<Department> deptList = departmentService.findAll();
// 添加部门节点
builder.append(deptList, (dept, node) -> {
node.setId(dept.getId())
.setParentId(dept.getParentId())
.setName(dept.getDeptName())
.setWeight(dept.getOrderNum())
.putExtra("code", dept.getDeptCode())
.putExtra("leader", dept.getLeaderName());
});
// 构建部门树
MapTree<Long> deptTree = builder.build();
// 输出部门树
Console.log("部门树: {}", deptTree);
# 3. 构建分类树
// 创建配置
TreeNodeConfig config = new TreeNodeConfig();
config.setDeep(2); // 限制深度为2层
// 创建TreeBuilder
TreeBuilder<String> builder = TreeBuilder.of("0", config);
// 分类数据
List<Category> categoryList = categoryService.findAll();
// 添加分类节点
builder.append(categoryList, (category, node) -> {
node.setId(category.getCatId())
.setParentId(category.getParentId())
.setName(category.getCatName())
.setWeight(category.getSortOrder())
.putExtra("level", category.getLevel())
.putExtra("enabled", category.getEnabled());
});
// 构建分类树
MapTree<String> categoryTree = builder.build();
// 输出分类树
Console.log("分类树: {}", categoryTree);
# 💡 注意事项
构建状态检查:
- 一旦调用
build()或buildList()方法,构建器将进入已构建状态 - 已构建状态下不能再添加节点,需要调用
reset()方法重置 - 调用
reset()会清空所有节点和构建状态
- 一旦调用
根节点处理:
- 构建器会自动处理根节点,如果添加的节点中包含根节点ID,会复用该节点
- 根节点的ID在创建构建器时指定,通常为0或"0"
节点转换器:
- 节点转换器用于将Bean转换为节点,需要实现
NodeParser接口 - 转换器中可以设置节点的各种属性,包括扩展属性
- 节点转换器用于将Bean转换为节点,需要实现
树剪枝:
- 如果配置了
deep属性,构建器会自动剪枝,只保留指定深度的节点 - 剪枝操作会在构建完成后执行
- 如果配置了
性能考虑:
- 对于大量节点的场景,建议使用
append(Iterator)方法,减少内存占用 - 构建完成后,
idTreeMap会被清空,释放内存 - 可以使用
reset()方法复用构建器,避免重复创建对象
- 对于大量节点的场景,建议使用
泛型类型:
E是ID的类型,建议使用Long、String等不可变类型- 确保所有节点的ID类型一致,否则会导致类型转换错误
# 🚀 性能优化
- 对于大数据量场景,使用
Iterator方式添加节点,避免一次性加载所有数据到内存 - 合理设置
deep属性,避免构建过深的树结构 - 复用构建器实例,使用
reset()方法重置后再次使用 - 节点转换器中避免复杂计算,提高转换效率
- 对于固定的节点结构,考虑缓存转换结果
# 🔍 最佳实践
- 使用静态工厂方法:优先使用
of()静态方法创建构建器,代码更简洁 - 链式调用:利用链式调用特性,使代码更流畅易读
- 节点转换器复用:对于相同类型的Bean转换,复用节点转换器实例
- 合理配置深度:根据业务需求设置合适的递归深度
- 扩展属性使用:充分利用扩展属性,存储节点的额外信息
- 构建后释放资源:构建完成后,构建器会自动清空中间数据,无需手动处理
- 异常处理:在节点转换器中添加适当的异常处理,确保构建过程稳定
# 📝 总结
TreeBuilder 是 Hutool 树结构模块中的核心构建器类,提供了灵活、高效的树构建能力。它支持多种节点添加方式,包括直接添加节点、添加节点Map、添加Bean列表并转换等。通过链式调用和Builder模式,使得树构建过程更加流畅和直观。
无论是构建简单的菜单树,还是复杂的部门树、分类树,TreeBuilder 都能提供可靠的支持。它的泛型设计和节点转换器机制,使得它能够适应各种业务场景和数据结构。
在实际开发中,建议根据具体需求选择合适的节点添加方式,并合理配置树的深度和其他属性。通过TreeBuilder,可以快速构建出符合业务需求的树结构,提高开发效率和代码质量。