package com.demo;import org.springframework.util.CollectionUtils;import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 文件名:TreeUtil* 创建者:* 创建时间:2024-06-29* 描述:*/
public class TreeUtil {/*** 因为是工具类所以私有化此类构造*/private TreeUtil(){}/*** 通过递归方式构建树* @param list* @return* @param <T>*/public static <T> List<T> buildTreeByRecursion(List<T> list) {return buildTreeByRecursion(list, "id", "parentId", "children");}/*** 通过Map方式构建树* @param list* @return* @param <T>*/public static <T> List<T> buildTreeByMap(List<T> list) {return buildTreeByMap(list, "id", "parentId", "children", "root");}/*** 通过两层for循环方式构建树* @param list* @return* @param <T>*/public static <T> List<T> buildTreeByTwoLayersFor(List<T> list) {return buildTreeByTwoLayersFor(list, "id", "parentId", "children", "root");}/*** 递归方式构建树* @param list* @param idName id名称* @param parentIdName 父id名称* @param childrenName 子集* @return* @param <T>*/public static <T> List<T> buildTreeByRecursion(List<T> list, String idName, String parentIdName, String childrenName) {if (CollectionUtils.isEmpty(list)) {return Collections.emptyList();}List<T> returnList = new ArrayList<>();//获取list中所有的主键idList<String> ids = list.stream().map(o -> getFieldValue(o, idName).toString()).collect(Collectors.toList());for (T t : list) {String parentId = getFieldValue(t, parentIdName).toString();//如果是顶级节点, 遍历该父节点的所有子节点,因为顶层的parentId为0if (!ids.contains(parentId)) {buildTreeRecursive(list, t, idName, parentIdName, childrenName);returnList.add(t);}}if (returnList.isEmpty()) {returnList = list;}return returnList;}/*** 递归fn* @param list 分类表* @param node 子节点* @param idName* @param parentIdName* @param childrenName* @param <T>*/private static <T> void buildTreeRecursive(List<T> list, T node, String idName, String parentIdName, String childrenName) {// 得到子节点列表List<T> childList = getChildList(list, node, idName, parentIdName);setFieldValue(node, childList, childrenName);for (T child : childList) {if (hasChild(list, child, idName, parentIdName)) {buildTreeRecursive(list, child, idName, parentIdName, childrenName);}}}/*** 获取子节点列表* @param list* @param node* @param idName* @param parentIdName* @return* @param <T>*/private static <T> List<T> getChildList(List<T> list, T node, String idName, String parentIdName) {List<T> tlist = new ArrayList<>();String oId = getFieldValue(node, idName).toString();for (T child : list) {String tParentId = getFieldValue(child, parentIdName).toString();if (tParentId.equals(oId)) {tlist.add(child);}}return tlist;}/*** 判断是否有子节点* @param list* @param t* @param idName* @param parentIdName* @return* @param <T>*/private static <T> boolean hasChild(List<T> list, T t, String idName, String parentIdName) {return getChildList(list, t, idName, parentIdName).size() > 0;}/*** 通过Map方式构建树* @param list 列表* @param idName id名称* @param parentIdName 父id名称* @param childrenName 子节点列表名称* @param topParentIdVal 顶层节点父id的值* @return* @param <T>*/public static <T> List<T> buildTreeByMap(List<T> list,String idName,String parentIdName,String childrenName,String topParentIdVal) {if (CollectionUtils.isEmpty(list)) {return Collections.emptyList();}//根据parentId进行分组Map<String, List<T>> mapList = list.stream().collect(Collectors.groupingBy(o -> getFieldValue(o, parentIdName).toString()));//给每个节点设置子节点列表list.forEach(node -> setFieldValue(node, mapList.get(getFieldValue(node, idName).toString()), childrenName));return list.stream().filter(o -> topParentIdVal.equals(getFieldValue(o, parentIdName))).collect(Collectors.toList());}/*** 通过两层for循环方式构建树* @param list 列表* @param idName id名称* @param parentIdName 父id名称* @param childrenName 子节点列表名称* @param topParentIdVal 顶层节点父id的值* @return* @param <T>*/public static <T> List<T> buildTreeByTwoLayersFor(List<T> list, String idName, String parentIdName, String childrenName, String topParentIdVal) {List<T> resultList = new ArrayList<>();for (T node : list) {//如果是顶层节点if (topParentIdVal.equals(getFieldValue(node, parentIdName))) {resultList.add(node);}for (T child : list) {if (getFieldValue(child, parentIdName).equals(getFieldValue(node, idName))) {List<T> childrenList = (List<T>) getFieldValue(node, childrenName);if (CollectionUtils.isEmpty(childrenList)) {childrenList = new ArrayList<>();setFieldValue(node, childrenList, childrenName);}childrenList.add(child);}}}return resultList;}/*** 获取属性值* @param o 对象* @param fieldName 属性名* @return*/private static Object getFieldValue(Object o, String fieldName) {try {Class<?> oClass = o.getClass();Field field = oClass.getDeclaredField(fieldName);field.setAccessible(true);return field.get(o);} catch (NoSuchFieldException | IllegalAccessException e) {throw new RuntimeException(e);}}/*** 设置字段值* @param o 对象* @param val 值* @param fieldName 属性名*/private static void setFieldValue(Object o, Object val, String fieldName) {try {Class<?> oClass = o.getClass();Field field = oClass.getDeclaredField(fieldName);field.setAccessible(true);field.set(o, val);} catch (NoSuchFieldException | IllegalAccessException e) {throw new RuntimeException(e);}}
}
package com.demo;import cn.hutool.core.date.DateTime;
import com.cnpc.epai.assetcatalog.util.TreeUtil;
import lombok.Data;import java.util.ArrayList;
import java.util.List;public class Demo01 {public static void main(String[] args) {List<NodeTree> nodeTreeList = initializeListNodeTree();Long bing = System.currentTimeMillis();//通过map方式组装treeList<NodeTree> nodeMapList = TreeUtil.buildTreeByMap(nodeTreeList,"id", "parentId", "children", "root");Long end = System.currentTimeMillis();Long da = end-bing;System.out.println("nodeMapList 时间:"+da);
Long bing1 = System.currentTimeMillis();//通过递归方式构建树组装treeList<NodeTree> nodeFnList = TreeUtil.buildTreeByRecursion(nodeTreeList,"id", "parentId", "children");Long end1 = System.currentTimeMillis();Long da1 = end1-bing1;System.out.println("nodeFnList 时间:"+da1);
Long bing2 = System.currentTimeMillis();//通过两层for循环方式构建treeList<NodeTree> nodeToFnList = TreeUtil.buildTreeByTwoLayersFor(nodeTreeList,"id", "parentId", "children","root");Long end2 = System.currentTimeMillis();Long da2 = end2-bing2;System.out.println("nodeToFnList 时间:"+da2);
}/*** 初始化 NodeTree 列表* @return*/public static List<NodeTree> initializeListNodeTree(){List<NodeTree> treeList = new ArrayList<>();for(int j = 1 ; j <= 5 ; j++){NodeTree tree = new NodeTree();tree.setId(j+"");tree.setParentId("root");tree.setDateTime(new DateTime());treeList.add(tree);}for(int i = 6; i < 1000 ; i++){NodeTree tree = new NodeTree();tree.setId(i+"");tree.setParentId("1");tree.setDateTime(new DateTime());treeList.add(tree);}for(int i = 1000; i < 2000 ; i++){NodeTree tree = new NodeTree();tree.setId(i+"");tree.setParentId("2");tree.setDateTime(new DateTime());treeList.add(tree);}for(int i = 2000; i < 3000 ; i++){NodeTree tree = new NodeTree();tree.setId(i+"");tree.setParentId("3");tree.setDateTime(new DateTime());treeList.add(tree);}for(int i = 3000; i < 4000 ; i++){NodeTree tree = new NodeTree();tree.setId(i+"");tree.setParentId("4");tree.setDateTime(new DateTime());treeList.add(tree);}for(int i = 4000; i < 5000 ; i++){NodeTree tree = new NodeTree();tree.setId(i+"");tree.setParentId("5");tree.setDateTime(new DateTime());treeList.add(tree);}for(int i = 5000; i < 6000 ; i++){NodeTree tree = new NodeTree();tree.setId(i+"");tree.setParentId("6");tree.setDateTime(new DateTime());treeList.add(tree);}return treeList;}
}/*** 节点对象* 这个类用的 lombok 所以没有get、set方法*/
@Data
class NodeTree{private String id; //idprivate String parentId; //父节点idprivate DateTime dateTime; //时间,可用于排序private List<NodeTree> children; //子集
}