三步构建完美树节点,从此告别数据结构焦虑!

原创 首发于卫星

树结构数据在后台管理系统中非常常见,无论是菜单权限,部门组织,还是分类目录,都离不开树形数据的支撑。树节点是树结构的核心单元,它的设计直接决定了树的灵活性和功能,下面作者将用清晰的代码示例,带你快速在项目中进行树结构的构建和应用。

实体类

实体类主要字段如下

typescript 复制代码
@Data
public class SysDepartment {

    private String id;

    private String parentId;
    
    private String name;
}

节点类

TreeNode

java 复制代码
package top.walker.common.operate;

/**
 * @description: 需要构建OptionNode节点的实体,需要继承该接口
 * @author: Walker
 * @date: 2025-01-11 22:31:31
 * @version: 1.0.0
 */
public interface TreeNode<T> {

    /**
     * id
     *
     * @return ID
     */
    public T getId();

    /**
     * parentId
     *
     * @return 父ID
     */
    public T getParentId();

    /**
     * label
     *
     * @return 名称
     */
    public String getName();

    /**
     * tag
     *
     * @return tag
     */
    public String getTag();
}

OptionNode

java 复制代码
package top.walker.common.operate;

import cn.hutool.core.collection.CollectionUtil;
import lombok.Data;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @description: 选项节点
 * @author: Walker
 * @date: 2025-01-11 22:30:30
 * @version: 1.0.0
 */
@Data
public class OptionNode<T> {

    public OptionNode() {
    }

    public OptionNode(T value, String label) {
        this.value = value;
        this.label = label;
    }

    public OptionNode(T value, String label, List<OptionNode<T>> children) {
        this.value = value;
        this.label = label;
        this.children = children;
    }

    public OptionNode(T value, String label, String tag) {
        this.value = value;
        this.label = label;
        this.tag = tag;
    }

    private T value;

    private String label;

    private String tag;

    private List<OptionNode<T>> children;

    /**
     * 构建树列表
     *
     * @param list 数据列表
     * @param <T>  value类型
     * @param <E>  数据列表中的类类型
     * @return 树列表
     */
    public static <T, E extends TreeNode<T>> List<OptionNode<T>> build(List<E> list) {
        if (CollectionUtil.isEmpty(list)) return Collections.emptyList();

        // 所有节点ID
        Set<T> ids = list.stream().map(TreeNode::getId).collect(Collectors.toSet());
        // 父节点ID
        Set<T> parentIds = list.stream().map(TreeNode::getParentId).collect(Collectors.toSet());
        // 根节点ID
        List<T> rootIds = CollectionUtil.subtractToList(parentIds, ids);

        // 递归生成树列表
        return rootIds.stream().flatMap(rootId -> buildTree(rootId, list).stream()).toList();
    }

    /**
     * 递归生成Option树节点
     * 使用该方法的实体必须实现TreeNode接口
     *
     * @param parentId 父ID
     * @param list     数据列表
     * @param <T>      value类型
     * @param <E>      数据列表中的类类型
     * @return 树列表
     */
    private static <T, E extends TreeNode<T>> List<OptionNode<T>> buildTree(T parentId, List<E> list) {
        return CollectionUtil.emptyIfNull(list).stream()
                .filter(item -> item.getParentId().equals(parentId))
                .map(item -> {
                    OptionNode<T> optionNode = new OptionNode<>(item.getId(), item.getName(), item.getTag());
                    List<OptionNode<T>> children = buildTree(item.getId(), list);
                    if (CollectionUtil.isNotEmpty(children)) {
                        optionNode.setChildren(children);
                    }
                    return optionNode;
                }).collect(Collectors.toList());
    }

}

使用示例

实体类实现 TreeNode接口,重写方法

java 复制代码
@Data
public class SysDepartment implements TreeNode<String> {

    private String id;

    private String parentId;
    
    private String name;

    @Override
    public String getTag() {
        return this.getName();
    }
}

构建节点树

java 复制代码
public List<OptionNode<String>> options() {
      List<SysDepartment> list = this.lambdaQuery()
              .eq(SysDepartment::getStatus, StatusEnum.NORMAL)
              .select(SysDepartment::getId, SysDepartment::getParentId, SysDepartment::getName)
              .orderByAsc(SysDepartment::getDisplayOrder).list();
      // 构建树列表
      return OptionNode.build(list);
  }

到此我们就实现了树节点的构建,后续使用时需要实现TreeNode,重写方法即可

结束!

相关推荐
腻害兔3 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:CRM 客户关系模块深度解析——从线索到回款,一套完整的 B2B 销售闭环是怎么搭的?
java·前端·javascript·vue.js·产品经理·ai编程
whyfail3 小时前
前端学 Spring Boot(2):一次点击,如何穿过整个后端?
前端·spring boot·后端
达子6664 小时前
第9章_HarmonyOs图解 用Java开发UI
java·ui·harmonyos
程序员cxuan5 小时前
Loop 还没玩明白,Graph Engineering 又火了。
人工智能·后端·程序员
龙虾PRO5 小时前
能源与股票量化核心差异,AI全链路落地实操手册
java·面试·职场和发展
llwszx5 小时前
【Java/Go后端手撸原生Agent(第九篇):Plan-and-Execute规划模式——从“走一步看一步“到“先谋后动“】
java·python·golang·agent开发·plan模式·规划执行模式
ZJH__GO6 小时前
网络编程v2--多客户端互通
java·运维·服务器·开发语言·计算机网络
uzong6 小时前
把一面面试总结做成一个 Skill:重复的事,就别每次都重写
后端·面试
霸道流氓气质6 小时前
SpringBoot中基于 AES-GCM + KMS 密钥管理的数据加解密 Starter 实践
java·数据库·spring boot
Gofarlic_OMS6 小时前
NX浮动许可调度黑名单机制,对比两款谁更合理
java·大数据·运维·开源·制造