TreeUtil实现列表转换树结构

文章介绍了Hutool库中用于构建和处理树状结构的方法,特别是如何根据数据库中的数据自定义字段来构建灵活的树结构。通过TreeNodeConfig配置,用户可以定义如父节点ID、权重等关键字段,以适应不同的数据库设计。文章还提供了个人使用示例,展示如何将Mapper查询结果转换为树形结构。

Hutool文档(官方文档, 最后有个人使用方法)

介绍

考虑到菜单等需求的普遍性,有用户提交了一个扩展性极好的树状结构实现。这种树状结构可以根据配置文件灵活的定义节点之间的关系,也能很好的兼容关系数据库中数据。实现

关系型数据库数据  <->  Tree  <->  JSON

树状结构中最大的问题就是关系问题,在数据库中,每条数据通过某个字段关联自己的父节点,每个业务中这个字段的名字都不同,如何解决这个问题呢?

PR的提供者提供了一种解决思路:自定义字段名,节点不再是一个bean,而是一个map,实现灵活的字段名定义。

使用

定义结构

我们假设要构建一个菜单,可以实现系统管理和店铺管理,菜单的样子如下:

系统管理
    |- 用户管理
    |- 添加用户

店铺管理
    |- 商品管理
    |- 添加商品

那这种结构如何保存在数据库中呢?一般是这样的:

idparentIdnameweight
10系统管理5
111用户管理10
1111用户添加11
20店铺管理5
212商品管理10
2212添加添加11

我们看到,每条数据根据parentId相互关联并表示层级关系,parentId在这里也叫外键。

构建Tree

// 构建node列表
List<TreeNode<String>> nodeList = CollUtil.newArrayList();

nodeList.add(new TreeNode<>("1", "0", "系统管理", 5));
nodeList.add(new TreeNode<>("11", "1", "用户管理", 222222));
nodeList.add(new TreeNode<>("111", "11", "用户添加", 0));
nodeList.add(new TreeNode<>("2", "0", "店铺管理", 1));
nodeList.add(new TreeNode<>("21", "2", "商品管理", 44));
nodeList.add(new TreeNode<>("221", "2", "商品管理2", 2));

TreeNode表示一个抽象的节点,也表示数据库中一行数据。 如果有其它数据,可以调用setExtra添加扩展字段。

// 0表示最顶层的id是0
List<Tree<String>> treeList = TreeUtil.build(nodeList, "0");

因为两个Tree是平级的,再没有上层节点,因此为List。

自定义字段名

//配置
TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
// 自定义属性名 都要默认值的
treeNodeConfig.setWeightKey("order");
treeNodeConfig.setIdKey("rid");
// 最大递归深度
treeNodeConfig.setDeep(3);

//转换器
List<Tree<String>> treeNodes = TreeUtil.build(nodeList, "0", treeNodeConfig,
        (treeNode, tree) -> {
            tree.setId(treeNode.getId());
            tree.setParentId(treeNode.getParentId());
            tree.setWeight(treeNode.getWeight());
            tree.setName(treeNode.getName());
            // 扩展属性 ...
            tree.putExtra("extraField", 666);
            tree.putExtra("other", new Object());
        });

通过TreeNodeConfig我们可以自定义节点的名称、关系节点id名称,这样就可以和不同的数据库做对应。

个人使用方法

@Override
public DataResult getTreeList(AreaNewReqVo vo) {
    List<AreaNew> list = areaNewMapper.getList(vo);
    TreeNodeConfig config = new TreeNodeConfig();
    config.setParentIdKey("pid");
    config.setWeightKey("sort");
    List<Tree<Integer>> build = TreeUtil.build(list, 0, config, (obj, tree) -> {
        tree.putExtra("id", obj.getId());
        tree.putExtra("pid", obj.getPid());
        tree.putExtra("sort", obj.getSort());
        tree.putExtra("level", obj.getLevel());
        tree.putExtra("icon", obj.getAreaName());
        tree.putExtra("pid", obj.getPid());
        tree.putExtra("code",obj.getCode());
    });
    DataResult dataResult =new DataResult();
    dataResult.setDataSingle(build);
    return dataResult;
}

Mapper查出所需列表

new个TreeNodeConfig(节点配置对象),然后配置判断父级id的是哪个字段 列:“pid”

List<Tree<Integer>> build = TreeUtil.build(list, 0, config, (obj, tree) -> {

TreeUtil.build的四个参数 1,mapper查出的list 2、rootId 表示节点的根部也叫起始ID 3、刚才配置的对象 4、匿名方法

​ 匿名方法内被就是把每个节点需要返回的字段列出来 按上面的格式

### Java实现树形结构的 `buildTree` 方法 在Java中构建树形结构,通常需要定义一个节点类(如 `TreeNode`),并提供一个工具方法(如 `buildTree`)来将扁平化的数据转换为树形结构。以下是一个完整的实现示例,包括 `TreeNode` 类和 `TreeUtil` 工具类的设计与实现[^1]。 #### 1. TreeNode 类定义 `TreeNode` 是树形结构的基本单元,包含节点的唯一标识符(`id`)、父节点标识符(`parentId`)、节点值(`value`)以及子节点列表(`children`)。 ```java public class TreeNode<T> { private T id; // 节点ID private T parentId; // 父节点ID private String value; // 节点值 private List<TreeNode<T>> children; // 子节点列表 public TreeNode(T id, T parentId, String value) { this.id = id; this.parentId = parentId; this.value = value; this.children = new ArrayList<>(); } // Getter 和 Setter 方法 public T getId() { return id; } public void setId(T id) { this.id = id; } public T getParentId() { return parentId; } public void setParentId(T parentId) { this.parentId = parentId; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public List<TreeNode<T>> getChildren() { return children; } public void setChildren(List<TreeNode<T>> children) { this.children = children; } @Override public String toString() { return "TreeNode{" + "id=" + id + ", parentId=" + parentId + ", value='" + value + '\'' + ", children=" + children + '}'; } } ``` #### 2. TreeUtil 工具类实现 `buildTree` 方法 `TreeUtil` 提供了一个静态方法 `buildTree`,用于将扁平化的节点列表转换为树形结构。通过递归查找每个节点的子节点,并将其添加到父节点的 `children` 列表中。 ```java import java.util.*; import java.util.stream.Collectors; public class TreeUtil<T> { public static <T> List<TreeNode<T>> buildTree(List<TreeNode<T>> nodes) { if (nodes == null || nodes.isEmpty()) { return Collections.emptyList(); } // 使用Map存储所有节点,便于快速查找 Map<T, TreeNode<T>> nodeMap = nodes.stream() .collect(Collectors.toMap(TreeNode::getId, node -> node)); List<TreeNode<T>> tree = new ArrayList<>(); for (TreeNode<T> node : nodes) { // 如果节点的parentId为空或不存在于nodeMap中,则为根节点 if (node.getParentId() == null || !nodeMap.containsKey(node.getParentId())) { tree.add(node); } else { // 将当前节点添加到其父节点的children列表中 TreeNode<T> parentNode = nodeMap.get(node.getParentId()); if (parentNode != null) { parentNode.getChildren().add(node); } } } return tree; } } ``` #### 3. 示例代码 以下是一个完整的示例,展示如何使用上述代码构建树形结构。 ```java import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<TreeNode<Integer>> nodes = Arrays.asList( new TreeNode<>(1, null, "Root"), new TreeNode<>(2, 1, "Child 1"), new TreeNode<>(3, 1, "Child 2"), new TreeNode<>(4, 2, "Grandchild 1"), new TreeNode<>(5, 2, "Grandchild 2") ); List<TreeNode<Integer>> tree = TreeUtil.buildTree(nodes); // 打印树形结构 printTree(tree, 0); } private static void printTree(List<TreeNode<Integer>> nodes, int level) { for (TreeNode<Integer> node : nodes) { System.out.println(" ".repeat(level * 4) + node.getValue()); printTree(node.getChildren(), level + 1); } } } ``` #### 4. 输出结果 运行上述代码后,输出结果如下: ``` Root Child 1 Grandchild 1 Grandchild 2 Child 2 ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值