TreeUtils.java
package com.pit.dds.utils.tree;
import cn.hutool.core.util.StrUtil;
import org.springframework.util.StringUtils;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
public class TreeUtil{
/**
* 转树结构
* @param treeList
* @return
*/
public static List<Tree> getZreeList(List<? extends Tree> treeList) {
if(treeList == null || treeList.size() == 0)
{
return new ArrayList<>();
}
List<Tree> parents = getParents(treeList);
for (Tree p : parents) {
List<? extends Tree> childPerms = getChildPerms(treeList, p.getId());
p.setChildren(childPerms);
}
return parents;
}
/**
* 获取顶层父节点id
* @param treeList
* @return
*/
private static List<Tree> getParents(List<? extends Tree> treeList) {
if (treeList == null || treeList.size() < 1) {
return Collections.emptyList();
}
List<String> ids = treeList.stream().map(i -> i.getId())
.filter(StrUtil::isNotEmpty).distinct()
.collect(Collectors.toList());
//pid在ids里面找不到的
return treeList.stream().filter(i -> !ids.contains(i.getPId()))
.collect(Collectors.toList());
}
/**
* 根据父节点的ID获取所有子节点
* @param list 分类表
* @param pid 传入的父节点ID
* @return String
*/
private static List<? extends Tree> getChildPerms(List<? extends Tree> list, String pid) {
pid = null == pid ? "" : pid;
List<Tree> returnList = new ArrayList<>();
for (Tree tree : list) {
if (pid.equals(tree.getPId())) {
returnList.add(tree);
recursionFn(list, tree);
}
}
return returnList;
}
/**
* 得到子节点列表
*/
private static List<? extends Tree> getChildList(List<? extends Tree> list, Tree t) {
List<Tree> clist = new ArrayList<>();
for (Tree tree : list) {
if (t.getId().equals(tree.getPId())) {
clist.add(tree);
}
}
return clist;
}
/**
* 判断是否有子节点
*/
private static boolean hasChild(List<? extends Tree> list, Tree t) {
for (Tree tree : list) {
if (t.getId().equals(tree.getPId())) {
return true;
}
}
return false;
}
/**
* 递归列表
* @param list
* @param t
*/
private static void recursionFn(List<? extends Tree> list, Tree t) {
List<? extends Tree> childList = getChildList(list, t);
t.setChildren(childList);
for (Tree tChild : childList) {
if (hasChild(list, tChild)) {
for (Tree tree : childList) {
recursionFn(list, tree);
}
}else{
tChild.setChildren(new ArrayList<>());
}
}
}
/**
* 集合转树结构
*
* @param collection 目标集合
* @param clazz 集合元素类型
* @return 转换后的树形结构
*/
public static <T> Collection<T> toTree(@NotNull Collection<T> collection, @NotNull Class<T> clazz) {
return toTree(collection, null, null, null, clazz);
}
/**
* 集合转树结构
* @param collection 目标集合
* @param id 节点编号字段名称
* @param parent 父节点编号字段名称
* @param children 子节点集合属性名称
* @param clazz 集合元素类型
* @return 转换后的树形结构
*/
public static <T> Collection<T> toTree(@NotNull Collection<T> collection, String id, String parent, String children, @NotNull Class<T> clazz) {
try {
if (collection == null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
if (StringUtils.isEmpty(id)) id = "id"; // 如果被依赖字段名称为空则默认为id
if (StringUtils.isEmpty(parent)) parent = "parent"; // 如果依赖字段为空则默认为parent
if (StringUtils.isEmpty(children)) children = "children"; // 如果子节点集合属性名称为空则默认为children
// 初始化根节点集合, 支持 Set 和 List
Collection<T> roots;
if (collection.getClass().isAssignableFrom(Set.class)) {
roots = new HashSet<>();
} else {
roots = new ArrayList<>();
}
// 获取 id 字段, 从当前对象或其父类
Field idField;
try {
idField = clazz.getDeclaredField(id);
} catch (NoSuchFieldException e1) {
idField = clazz.getSuperclass().getDeclaredField(id);
}
// 获取 parentId 字段, 从当前对象或其父类
Field parentField;
try {
parentField = clazz.getDeclaredField(parent);
} catch (NoSuchFieldException e1) {
parentField = clazz.getSuperclass().getDeclaredField(parent);
}
// 获取 children 字段, 从当前对象或其父类
Field childrenField;
try {
childrenField = clazz.getDeclaredField(children);
} catch (NoSuchFieldException e1) {
childrenField = clazz.getSuperclass().getDeclaredField(children);
}
// 设置为可访问
idField.setAccessible(true);
parentField.setAccessible(true);
childrenField.setAccessible(true);
// 找出所有的根节点
for (T c : collection) {
Object parentId = parentField.get(c);
if (isRootNode(parentId)) {
roots.add(c);
}
}
// 从目标集合移除所有根节点
collection.removeAll(roots);
// 遍历根节点, 依次添加子节点
for (T root : roots) {
addChild(root, collection, idField, parentField, childrenField);
}
// 关闭可访问
idField.setAccessible(false);
parentField.setAccessible(false);
childrenField.setAccessible(false);
return roots;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 为目标节点添加孩子节点
*
* @param node 目标节点
* @param collection 目标集合
* @param idField ID 字段
* @param parentField 父节点字段
* @param childrenField 字节点字段
*/
private static <T> void addChild(@NotNull T node, @NotNull Collection<T> collection, @NotNull Field idField, @NotNull Field parentField, @NotNull Field childrenField) throws IllegalAccessException {
Object id = idField.get(node);
Collection<T> children = (Collection<T>) childrenField.get(node);
// 如果子节点的集合为 null, 初始化孩子集合
if (children == null) {
if (collection.getClass().isAssignableFrom(Set.class)) {
children = new HashSet<>();
} else children = new ArrayList<>();
}
for (T t : collection) {
Object o = parentField.get(t);
if (id.equals(o)) {
// 将当前节点添加到目标节点的孩子节点
children.add(t);
// 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
childrenField.set(node, children);
// 递归添加孩子节点
addChild(t, collection, idField, parentField, childrenField);
}
}
}
/**
* 判断是否是根节点, 判断方式为: 父节点编号为空或为 0, 则认为是根节点. 此处的判断应根据自己的业务数据而定.
* @param parentId 父节点编号
* @return 是否是根节点
*/
private static boolean isRootNode(Object parentId) {
boolean flag = false;
if (parentId == null) {
flag = true;
} else if (parentId instanceof String && (StringUtils.isEmpty(parentId) || parentId.equals("0"))) {
flag = true;
} else if (parentId instanceof Integer && Integer.valueOf(0).equals(parentId)) {
flag = true;
}
return flag;
}
}
基础Tree.java
package com.pit.dds.utils.tree;
import java.util.List;
/**
* 树结构接口
*/
public interface Tree {
String getId();
String getPId();
List<? extends Tree> getChildren();
void setChildren(List<? extends Tree> list);
}
自定义Tree实体.java
PitDdsDrawingCatalogTree 继承对应的业务实体,实现返回的树结构包含业务表的数据。 再实现Tree.java
package com.pit.dds.drawing.vo;
import cn.hutool.core.convert.Convert;
import com.pit.dds.drawing.entity.PitDdsDrawingCatalogEntity;
import com.pit.dds.utils.tree.Tree;
import lombok.Data;
import java.util.List;
@Data
public class PitDdsDrawingCatalogTree extends PitDdsDrawingCatalogEntity implements Tree {
List<? extends Tree> Children;
@Override
public String getId() {
//取主键ID
return super.getDrawingCatalogId();
}
@Override
public String getPId() {
//取父级ID
return Convert.toStr(super.getUpDrawingCatalogId());
}
Boolean IsOpen = true;
}
业务实现
1、mybatis查询 resultType用自定义的PitDdsDrawingCatalogTree实体接收
2、TreeUtil.getZreeList(ztreeList);