449 lines
18 KiB
Java
449 lines
18 KiB
Java
package net.shapelight.common.utils;
|
||
|
||
|
||
import com.alibaba.fastjson.JSON;
|
||
import com.alibaba.fastjson.JSONArray;
|
||
import com.alibaba.fastjson.JSONObject;
|
||
import net.shapelight.modules.sys.entity.SysDeptEntity;
|
||
import org.apache.commons.lang.StringUtils;
|
||
|
||
import javax.validation.constraints.NotNull;
|
||
import java.lang.reflect.Field;
|
||
import java.util.*;
|
||
|
||
public class TreeUtils {
|
||
|
||
/*public static boolean isEmpty(String str) {
|
||
if (str != null && !"".equals(str.trim())) {
|
||
return false;
|
||
}
|
||
return true;
|
||
}*/
|
||
|
||
/**
|
||
* 集合转树结构
|
||
*
|
||
* @param collection 目标集合
|
||
* @param clazz 集合元素类型
|
||
* @param <T>
|
||
* @return
|
||
*/
|
||
public static <T> Collection<T> toTree(@NotNull Collection<T> collection, @NotNull Class<T> clazz) {
|
||
return toTree(collection, null, null, null, clazz);
|
||
}
|
||
|
||
/**
|
||
* 集合转树结构,注意,使用此方法,则集合元素必须继承ITree接口
|
||
*
|
||
* @param collection 目标集合
|
||
* @param <T>
|
||
* @return
|
||
*/
|
||
public static <T extends ITree> Collection<T> toTree(@NotNull Collection<T> collection) {
|
||
try {
|
||
if (collection == null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
|
||
// 找出所有的根节点
|
||
Collection<T> roots = null;
|
||
if (collection.getClass().isAssignableFrom(Set.class)) roots = new HashSet<>();
|
||
else roots = new ArrayList<>();
|
||
for (T tree : collection) {
|
||
Object o = ITree.class.getMethod("getParent").invoke(tree);
|
||
if (o instanceof String) {
|
||
if (StringUtils.isEmpty((String) o)) {
|
||
roots.add(tree);
|
||
}
|
||
} else if (o == null) {
|
||
roots.add(tree);
|
||
}
|
||
}
|
||
// 从目标集合移除所有的根节点
|
||
collection.removeAll(roots);
|
||
// 为根节点添加孩子节点
|
||
for (T tree : roots) {
|
||
addChild(tree, collection);
|
||
}
|
||
return roots;
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 集合转树结构
|
||
*
|
||
* @param collection 目标集合
|
||
* @param id 被依赖字段名称
|
||
* @param parent 依赖字段名称
|
||
* @param children 子节点集合属性名称
|
||
* @param clazz 集合元素类型
|
||
* @param <T>
|
||
* @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
|
||
Collection<T> roots = null;// 初始化根节点集合
|
||
if (collection.getClass().isAssignableFrom(Set.class))
|
||
roots = new HashSet<>();// 如果目标节点是一个set集合,则初始化根节点集合为hashset
|
||
else roots = new ArrayList<>();// 否则初始化为Arraylist,
|
||
// 这里集合初始化只分2中,要么是hashset,要么ArrayList,因为这两种最常用,其他不常用的摒弃
|
||
Field idField = null;
|
||
try {
|
||
idField = clazz.getDeclaredField(id);// 获取依赖字段
|
||
} catch (NoSuchFieldException e1) {
|
||
idField = clazz.getSuperclass().getDeclaredField(id);
|
||
}
|
||
Field parentField = null;
|
||
try {
|
||
parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
|
||
} catch (NoSuchFieldException e1) {
|
||
parentField = clazz.getSuperclass().getDeclaredField(parent);
|
||
}
|
||
Field childrenField = null;// 获取孩子字段
|
||
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 o = parentField.get(c);
|
||
if (o instanceof String) {
|
||
if (StringUtils.isEmpty((String) o)) {// 如果父节点为空则说明是根节点,添加到根节点集合
|
||
roots.add(c);
|
||
}
|
||
} else {
|
||
if (o == null) {
|
||
roots.add(c);
|
||
}
|
||
}
|
||
}*/
|
||
for (T c : collection) {
|
||
Object o = parentField.get(c);
|
||
if (o instanceof Long) {
|
||
if (((Long)o).longValue() == 0L) {// 如果父节点为空则说明是根节点,添加到根节点集合
|
||
roots.add(c);
|
||
}
|
||
} else {
|
||
if (o == null) {
|
||
roots.add(c);
|
||
}
|
||
}
|
||
}
|
||
// 从目标集合移除所有根节点
|
||
collection.removeAll(roots);
|
||
for (T c : roots) {// 遍历根节点,依次添加子节点
|
||
addChild(c, collection, idField, parentField, childrenField);
|
||
}
|
||
// 关闭可访问
|
||
idField.setAccessible(false);
|
||
parentField.setAccessible(false);
|
||
childrenField.setAccessible(false);
|
||
return roots;
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
public static <T extends ITree> void addChild(T tree, Collection<T> collection) {
|
||
try {
|
||
Object id = ITree.class.getMethod("getId").invoke(tree);
|
||
Collection<T> children = (Collection<T>) ITree.class.getMethod("getChildren").invoke(tree);
|
||
for (T cc : collection) {
|
||
Object o = ITree.class.getMethod("getParent").invoke(cc);
|
||
if (id.equals(o)) {// 如果当前节点的被依赖值和目标节点的被依赖值相等,则说明,当前节点是目标节点的子节点
|
||
if (children == null) {// 如果目标节点的孩子集合为null,初始化目标节点的孩子集合
|
||
if (collection.getClass().isAssignableFrom(Set.class)) {// 如果目标集合是一个set集合,则初始化目标节点的孩子节点集合为set
|
||
children = new HashSet<>();
|
||
} else children = new ArrayList<>();// 否则初始化为list
|
||
}
|
||
// 将当前节点添加到目标节点的孩子节点
|
||
children.add(cc);
|
||
// 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
|
||
ITree.class.getMethod("setChildren", Collection.class).invoke(tree, children);
|
||
// 递归添加孩子节点
|
||
addChild(cc, collection);
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为目标节点添加孩子节点,此方法为私有,不能为公开,否则类修改信息无法恢复,后面有公开方法,其专门为目标节点添加子节点
|
||
*
|
||
* @param c 目标节点
|
||
* @param collection 目标集合
|
||
* @param idField
|
||
* @param parentField
|
||
* @param childrenField
|
||
* @param <T>
|
||
* @throws IllegalAccessException
|
||
*/
|
||
private static <T> void addChild(@NotNull T c, @NotNull Collection<T> collection, @NotNull Field idField, @NotNull Field parentField, @NotNull Field childrenField) throws IllegalAccessException {
|
||
Object id = idField.get(c);// 获取目标节点的被依赖值
|
||
Collection<T> children = (Collection<T>) childrenField.get(c);// 获取目标节点的孩子列表
|
||
for (T cc : collection) {// 遍历目标集合
|
||
Object o = parentField.get(cc);// 获取当前节点的依赖值
|
||
if (id.equals(o)) {// 如果当前节点的被依赖值和目标节点的被依赖值相等,则说明,当前节点是目标节点的子节点
|
||
if (children == null) {// 如果目标节点的孩子集合为null,初始化目标节点的孩子集合
|
||
if (collection.getClass().isAssignableFrom(Set.class)) {// 如果目标集合是一个set集合,则初始化目标节点的孩子节点集合为set
|
||
children = new HashSet<>();
|
||
} else children = new ArrayList<>();// 否则初始化为list
|
||
}
|
||
// 将当前节点添加到目标节点的孩子节点
|
||
children.add(cc);
|
||
// 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
|
||
childrenField.set(c, children);
|
||
// 递归添加孩子节点
|
||
addChild(cc, collection, idField, parentField, childrenField);
|
||
}
|
||
}
|
||
// 特别说明:大家可以看到此递归没有明显出口,其出口就是是否当前节点的依赖值和目标节点的被依赖值一样,一样就递归,不一样进不了if,自然出递归
|
||
// 此工具类自我感觉是最简单的,最实用的工具类,我看网上许多人写的,都是云的雾的,本来也想借鉴,但是实在没一个能看的感觉思路清晰,没办法,自己动手造轮子
|
||
}
|
||
|
||
/**
|
||
* 为目标节点添加孩子
|
||
*
|
||
* @param c 目标节点
|
||
* @param collection 目标集合
|
||
* @param id 被依赖字段名
|
||
* @param parent 依赖字段名
|
||
* @param children 孩子节点字段名
|
||
* @param clazz 集合元素所在类别
|
||
* @param <T>
|
||
*/
|
||
public static <T> void addChild(@NotNull T c, @NotNull Collection<T> collection, String id, String parent, String children, @NotNull Class<T> clazz) {
|
||
try {
|
||
if (collection == null || collection.isEmpty()) return;// 如果目标集合为空,直接返回一个空树
|
||
if (StringUtils.isEmpty(id)) id = "id";// 如果被依赖字段名称为空则默认为id
|
||
if (StringUtils.isEmpty(parent)) parent = "parent";// 如果依赖字段为空则默认为parent
|
||
if (StringUtils.isEmpty(children)) children = "children";// 如果子节点集合属性名称为空则默认为children
|
||
Field idField = null;
|
||
try {
|
||
idField = clazz.getDeclaredField(id);// 获取依赖字段
|
||
} catch (NoSuchFieldException e1) {
|
||
idField = clazz.getSuperclass().getDeclaredField(id);
|
||
}
|
||
Field parentField = null;
|
||
try {
|
||
parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
|
||
} catch (NoSuchFieldException e1) {
|
||
parentField = clazz.getSuperclass().getDeclaredField(parent);
|
||
}
|
||
Field childrenField = null;// 获取孩子字段
|
||
try {
|
||
childrenField = clazz.getDeclaredField(children);
|
||
} catch (NoSuchFieldException e1) {
|
||
childrenField = clazz.getSuperclass().getDeclaredField(children);
|
||
}
|
||
// 设置为可访问
|
||
idField.setAccessible(true);
|
||
parentField.setAccessible(true);
|
||
childrenField.setAccessible(true);
|
||
addChild(c, collection, idField, parentField, childrenField);
|
||
// 关闭可访问
|
||
idField.setAccessible(false);
|
||
parentField.setAccessible(false);
|
||
childrenField.setAccessible(false);
|
||
} catch (Exception e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为目标节点添加孩子
|
||
*
|
||
* @param c 目标节点
|
||
* @param collection 目标集合
|
||
* @param clazz 集合元素所在类型
|
||
* @param <T>
|
||
*/
|
||
public static <T> void addChild(@NotNull T c, @NotNull Collection<T> collection, @NotNull Class<T> clazz) {
|
||
addChild(c, collection, null, null, null, clazz);
|
||
}
|
||
|
||
|
||
|
||
|
||
/**
|
||
|
||
- listToTree
|
||
- <p>方法说明<p>
|
||
- 将JSONArray数组转为树状结构
|
||
- @param arr 需要转化的数据
|
||
- @param id 数据唯一的标识键值
|
||
- @param pid 父id唯一标识键值
|
||
- @param child 子节点键值
|
||
- @return JSONArray
|
||
*/
|
||
public static JSONArray listToTree(JSONArray arr, String id, String pid, String child){
|
||
JSONArray r = new JSONArray();
|
||
JSONObject hash = new JSONObject();
|
||
//将数组转为Object的形式,key为数组中的id
|
||
for(int i=0;i<arr.size();i++){
|
||
JSONObject json = (JSONObject) arr.get(i);
|
||
hash.put(json.getString(id), json);
|
||
}
|
||
//遍历结果集
|
||
for(int j=0;j<arr.size();j++){
|
||
//单条记录
|
||
JSONObject aVal = (JSONObject) arr.get(j);
|
||
//在hash中取出key为单条记录中pid的值
|
||
JSONObject hashVP = (JSONObject) hash.get(aVal.get(pid).toString());
|
||
//如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
|
||
if(hashVP!=null){
|
||
//检查是否有child属性
|
||
if(hashVP.get(child)!=null){
|
||
JSONArray ch = (JSONArray) hashVP.get(child);
|
||
ch.add(aVal);
|
||
hashVP.put(child, ch);
|
||
}else{
|
||
JSONArray ch = new JSONArray();
|
||
ch.add(aVal);
|
||
hashVP.put(child, ch);
|
||
}
|
||
}else{
|
||
r.add(aVal);
|
||
}
|
||
}
|
||
return r;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 树形数据转换
|
||
* @param {*} data
|
||
* @param {*} id
|
||
* @param {*} pid
|
||
*/
|
||
|
||
public static void main(String[] args) {
|
||
List<Menu> list = new ArrayList<>();
|
||
list.add(new Menu(1, 100));
|
||
list.add(new Menu(2, 100));
|
||
list.add(new Menu(3, 100));
|
||
list.add(new Menu(4, 1));
|
||
list.add(new Menu(5, 1));
|
||
list.add(new Menu(6, 1));
|
||
list.add(new Menu(7, 2));
|
||
list.add(new Menu(8, 2));
|
||
list.add(new Menu(9, 3));
|
||
list.add(new Menu(10, 4));
|
||
list.add(new Menu(11, 7));
|
||
list.add(new Menu(12, 5));
|
||
list.add(new Menu(13, 10));
|
||
list.add(new Menu(14, 8));
|
||
list.add(new Menu(15, 11));
|
||
list.add(new Menu(16, 12));
|
||
list.add(new Menu(17, 13));
|
||
|
||
List<SysDeptEntity> deptList = new ArrayList<>();
|
||
SysDeptEntity s = new SysDeptEntity();
|
||
s.setDeptId(2L);
|
||
s.setParentId(7L);
|
||
s.setName("系统管理");
|
||
deptList.add(s);
|
||
|
||
s = new SysDeptEntity();
|
||
s.setDeptId(6L);
|
||
s.setParentId(2L);
|
||
deptList.add(s);
|
||
|
||
s = new SysDeptEntity();
|
||
s.setDeptId(8L);
|
||
s.setParentId(6L);
|
||
deptList.add(s);
|
||
|
||
s = new SysDeptEntity();
|
||
s.setDeptId(9L);
|
||
s.setParentId(8L);
|
||
deptList.add(s);
|
||
|
||
//Collection<Menu> menus = TreeUtils.toTree(list, null, null, null, Menu.class);
|
||
//System.out.println(JSON.toJSONString(menus));
|
||
|
||
|
||
//Collection<SysDeptEntity> depts = TreeUtils.toTree(deptList, "deptId", "parentId", "childrenList", SysDeptEntity.class);
|
||
//System.out.println(JSON.toJSONString(depts));
|
||
|
||
|
||
|
||
/*List<SysDeptEntity> deptList = new ArrayList<>();
|
||
SysDeptEntity s = new SysDeptEntity();
|
||
s.setDeptId(2L);
|
||
s.setParentId(7L);
|
||
s.setName("系统管理");
|
||
deptList.add(s);
|
||
|
||
s = new SysDeptEntity();
|
||
s.setDeptId(6L);
|
||
s.setParentId(2L);
|
||
s.setName("用户管理");
|
||
deptList.add(s);*/
|
||
|
||
|
||
System.out.println(JSON.toJSONString(deptList));
|
||
JSONArray deptTree = listToTree(JSONArray.parseArray(JSON.toJSONString(deptList)),"deptId","parentId","childrenList");
|
||
System.out.println(JSON.toJSONString(deptTree));
|
||
|
||
System.out.println(JSON.toJSONString(list));
|
||
JSONArray listTree = listToTree(JSONArray.parseArray(JSON.toJSONString(list)),"id","parent","children");
|
||
System.out.println(JSON.toJSONString(listTree));
|
||
|
||
}
|
||
}
|
||
|
||
class Menu {
|
||
private Integer id;
|
||
private Integer parent;
|
||
private List<Menu> children;
|
||
|
||
public Menu() {
|
||
}
|
||
|
||
public Menu(Integer id, Integer parent) {
|
||
this.id = id;
|
||
this.parent = parent;
|
||
}
|
||
|
||
public List<Menu> getChildren() {
|
||
return children;
|
||
}
|
||
|
||
public void setChildren(List<Menu> children) {
|
||
this.children = children;
|
||
}
|
||
|
||
public Integer getId() {
|
||
return id;
|
||
}
|
||
|
||
public void setId(Integer id) {
|
||
this.id = id;
|
||
}
|
||
|
||
public Integer getParent() {
|
||
return parent;
|
||
}
|
||
|
||
public void setParent(Integer parent) {
|
||
this.parent = parent;
|
||
}
|
||
}
|
||
|
||
|