首页 > 编程语言 >java List子父级集合转List树工具类

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

时间:2024-07-05 14:31:12浏览次数:12  
标签:idName java String parentIdName 父级 List list param

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循环最慢

标签:idName,java,String,parentIdName,父级,List,list,param
From: https://www.cnblogs.com/ningbeibei/p/18285757

相关文章

  • 温故而知新,详细讲讲JavaScript的防抖与节流
    前言:哈喽,大家好,我是前端菜鸟的自我修养!今天给大家详细讲讲JavaScript的防抖与节流,包含二者的区别与优缺点、使用场景,并提供具体代码帮助大家深入理解,彻底掌握!原创不易,如果能帮助到带大家,欢迎收藏+关注哦......
  • [Java] Java 关键字 : transient
    0序Java中的transient关键字,transient是短暂的意思。对于transient修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。1序列化Java中对象的序列化指的是......
  • JAVA API营业执照识别、企业工商信息查询
    现如今,随着互联网技术的不断成熟与智能应用的不断普及,人们对于营业执照识别技术的需求越来越大。像工商、税务、银行、网上注册等一些应用场景在进行经营活动和业务中,都需要对营业执照的信息进行采集和录入,无论是个人业务还是企业对公业务都在逐步的互联网化。对于平台......
  • 2.2 实验三、自动生成语法分析程序(JavaCUP)
    help-assignment2.3实验三、自动生成语法分析程序(JavaCUP)实验三要求你下载一个语法分析程序自动生成工具JavaCUP,利用该工具自动产生一个Oberon-0语言的语法分析和语法制导翻译程序;生成的程序源代码是以Java语言编写的。2.3.1实验步骤3.1、下载自动生成工具Java......
  • 微信小程序源码-基于Java后端的餐厅点餐系统毕业设计(附源码+论文)
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......
  • 微信小程序源码-基于Java后端的球馆预约系统毕业设计(附源码+论文)
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......
  • 微信小程序源码-基于Java后端的英语互助系统毕业设计(附源码+论文)
    大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。......
  • Java 方法中循环调用具有事务的方法
    在Java中,循环调用一个具有事务的方法时,需要特别注意事务的边界和管理。通常,事务的边界是由框架(如Spring)来控制的,确保方法执行时数据的完整性和一致性。然而,在循环中调用事务方法时,每个调用都可以被视为独立的事务,除非特别配置以允许跨多个方法调用共享同一事务。1.Java方法中循......
  • Java定时任务CRON表达式
    CRON表达式@PostMapping("/schedule")publicStringscheduleTaskWithCron(@RequestParamintsecond,@RequestParamintminute,@RequestParaminthour,@RequestParamintdayOfMonth,......
  • 18. JAVA 多线程锁介绍
    1.前言本节内容主要是对Java多线程锁进行介绍,是对锁的一个全方位的概述,为我们对后续深入学习不同的锁的使用方法奠定一个良好的基础。本节内容的知识点如下:乐观锁与悲观锁的概念,以及两种锁之间的区别,这是并发编程中经常涉及到的知识点,这是本节课程的核心知识点,是热度很高......