原创 首发于卫星
树结构数据在后台管理系统中非常常见,无论是菜单权限,部门组织,还是分类目录,都离不开树形数据的支撑。树节点是树结构的核心单元,它的设计直接决定了树的灵活性和功能,下面作者将用清晰的代码示例,带你快速在项目中进行树结构的构建和应用。
实体类
实体类主要字段如下
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,重写方法即可
结束!