java List集合转Tree集合
1.创建泛型工具类
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中所有的主键id List<String> ids = list.stream().map(o -> getFieldValue(o, idName).toString()).collect(Collectors.toList()); for (T t : list) { String parentId = getFieldValue(t, parentIdName).toString(); //如果是顶级节点, 遍历该父节点的所有子节点,因为顶层的parentId为0 if (!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); } } }
2.创建测试类
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方式组装tree List<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(); //通过递归方式构建树组装tree List<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循环方式构建tree List<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; //id private String parentId; //父节点id private DateTime dateTime; //时间,可用于排序 private List<NodeTree> children; //子集 }
3.测试list转还tree的耗时
- map的转换方式效率最高
- 递归方式其次
- 两层for循环最慢