java List子父级集合转List树工具类

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的耗时

  1. map的转换方式效率最高
  2. 递归方式其次
  3. 两层for循环最慢

posted @ 2024-07-05 14:23  晓海无涯苦揍舟  阅读(2)  评论(0编辑  收藏  举报