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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|