项目中,经常有用到树形结构数据,例如构建菜单、构建机构树等。这里分享构建树形结构,根据父级获取所有子级,以及子级获取父级的一种方法。
首先,表结构需要包括id以及父级parentId,如下为返回参考的DTO类:
@Data
public class TreeDTO {
@ApiModelProperty(notes = "id")
private String id;
@ApiModelProperty(notes = "名称")
private String name;
@ApiModelProperty(notes = "父级Id")
private String parentId;
@ApiModelProperty(notes = "下级分类")
private List children;
}
目录
一、构建树形结构工具类
二、 根据父级获取所有子级包括其自身
三、子级获取父级
/* 把列表转换为树结构
*
* @param originalList 原始list数据
* @param idFieldName 作为唯一标示的字段名称
* @param pidFieldName 父节点标识字段名
* @param childrenFieldName 子节点(列表)标识字段名
* @return 树结构列表
*/
public static List list2TreeList(List originalList, String idFieldName, String pidFieldName,
String childrenFieldName) {
// 获取根节点,即找出父节点为空的对象
List rootNodeList = new ArrayList<>();
for (T t : originalList) {
String parentId = null;
try {
parentId = BeanUtils.getProperty(t, pidFieldName);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
if (StringUtils.isBlank(parentId)) {
rootNodeList.add(0, t);
}
}
// 将根节点从原始list移除,减少下次处理数据
originalList.removeAll(rootNodeList);
// 递归封装树
try {
packTree(rootNodeList, originalList, idFieldName, pidFieldName, childrenFieldName);
} catch (Exception e) {
e.printStackTrace();
}
return rootNodeList;
}
/**
* 封装树(向下递归)
*
* @param parentNodeList 要封装为树的父节点对象集合
* @param originalList 原始list数据
* @param keyName 作为唯一标示的字段名称
* @param pidFieldName 父节点标识字段名
* @param childrenFieldName 子节点(列表)标识字段名
*/
private static void packTree(List parentNodeList, List originalList, String keyName,
String pidFieldName, String childrenFieldName) throws Exception {
for (T parentNode : parentNodeList) {
// 找到当前父节点的子节点列表
List children = packChildren(parentNode, originalList, keyName, pidFieldName, childrenFieldName);
if (children.isEmpty()) {
continue;
}
// 将当前父节点的子节点从原始list移除,减少下次处理数据
originalList.removeAll(children);
// 开始下次递归
packTree(children, originalList, keyName, pidFieldName, childrenFieldName);
}
}
/**
* 封装子对象
*
* @param parentNode 父节点对象
* @param originalList 原始list数据
* @param keyName 作为唯一标示的字段名称
* @param pidFieldName 父节点标识字段名
* @param childrenFieldName 子节点(列表)标识字段名
*/
private static List packChildren(T parentNode, List originalList, String keyName, String pidFieldName,
String childrenFieldName) throws Exception {
// 找到当前父节点下的子节点列表
List childNodeList = new ArrayList<>();
String parentId = BeanUtils.getProperty(parentNode, keyName);
for (T t : originalList) {
String childNodeParentId = BeanUtils.getProperty(t, pidFieldName);
if (parentId.equals(childNodeParentId)) {
childNodeList.add(t);
}
}
// 将当前父节点下的子节点列表写入到当前父节点下(给子节点列表字段赋值)
if (!childNodeList.isEmpty()) {
FieldUtils.writeDeclaredField(parentNode, childrenFieldName, childNodeList, true);
}
return childNodeList;
}
构建树形结构,首先需要查询数据库,获取表信息
@Select("select t.id,t.parent_id,t.name from sys_tree t ")
List getTree();
public List getTree() {
// baseMapper.getTree() 为mapper层,获取表信息
List treeDTOS = baseMapper.getTree();
// 调用工具类方法 list2TreeList
List treeDTOTree = list2TreeList(treeDTOS,"id","parentId","children");
return treeDTOTree ;
}
/**
* 取指定菜单的所有下级菜单包括其自身
*
* @param treeId ID
* @return ID集合
*/
public List getAllChildrenIdWithParentId(String treeId) {
List trees= baseMapper.getTree();
List childTrees =new ArrayList<>();
// 递归
this.treeOrgChildren(childTrees, trees, treeId);
List uuids = new ArrayList<>();
for (TreeDTO treeDTO : childTrees) {
uuids.add(treeDTO .getId());
}
uuids.add(treeId);
return uuids;
}
/**
* 获取指定菜单的所有子节点
*
* @param childTrees 子节点收集器
* @param trees 当前菜单
* @param parentTreeId 父菜单ID
*/
private void treeOrgChildren(List childTrees, List trees, String parentTreeId) {
for (TreeDTO tree: trees) {
if (menu.getParentId() != null && menu.getParentId().equals(parentTreeId)) {
treeOrgChildren(childTrees, trees, tree.getId());
childTrees.add(tree);
}
}
}
/**
* 递归遍历获取指定菜单的所有父节点
*
* @param trees 当前菜单
* @param treeId 子菜单ID
*/
public List queryParentIds(String treeId, List trees) {
//递归获取父级ids,不包含自己
List parentIds = new ArrayList<>();
this.treeOrgParent(trees, treeId, parentIds);
return parentIds;
}
/**
* 递归获取父级ids
* @param trees
* @param treeId
* @param parentIds
*/
public void treeOrgParent(List trees, String treeId, List parentIds) {
for (TreeDTO tree : trees) {
if (StringUtils.isEmpty(tree.getParentId())) {
continue;
}
//判断是否有父节点
if (menuId.equals(tree.getId())) {
parentIds.add(tree.getParentId());
treeOrgParent(trees, tree.getParentId(), parentIds);
}
}
}