java中把一个list转tree的三种方法——工具类

这篇具有很好参考价值的文章主要介绍了java中把一个list转tree的三种方法——工具类。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

java中把一个list转tree的三种实现方法

如何使用:

如果你的类中主键名称为id,父节点id名称为parentId,子节点列表名称为children,数据库中顶层父节点id值为“0”,可以直接调用只需传入需要转换list的方法。否则需要传入相应的字段名称,或者修改代码。

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author :Kite
 * @Date : 2023/5/8 9:52
 */
public class TreeUtil {

    /**
     * 通过递归方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByRecursion(List<T> list) {
        return buildTreeByRecursion(list, "id", "parentId", "children");
    }

    /**
     * 通过Map方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByMap(List<T> list) {
        return buildTreeByMap(list, "id", "parentId", "children", "0");
    }

    /**
     * 通过两层for循环方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByTwoLayersFor(List<T> list) {
        return buildTreeByTwoLayersFor(list, "id", "parentId", "children", "0");
    }

    /**
     * 通过递归方式构建树
     *
     * @param list         列表
     * @param idName       id名称
     * @param parentIdName 父id名称
     * @param childrenName 子节点列表名称
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByRecursion(List<T> list, String idName, String parentIdName, String childrenName) {
        if (StringUtils.isBlank(idName) || StringUtils.isBlank(parentIdName) || StringUtils.isBlank(childrenName)) {
            return new ArrayList<>();
        }
        List<T> returnList = new ArrayList<>();
        //获取list中所有的主键id
        List<String> ids = list.stream().map(o -> getFieldValue(o, idName).toString()).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 子节点
     */
    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);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    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;
    }

    /**
     * 判断是否有子节点
     */
    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 {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByMap(List<T> list, String idName, String parentIdName, String childrenName, String topParentIdVal) {
        if (StringUtils.isBlank(idName) || StringUtils.isBlank(parentIdName) || StringUtils.isBlank(childrenName)) {
            return new ArrayList<>();
        }
        //根据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());
    }

    /**
     * 获取属性值
     *
     * @param o         对象
     * @param fieldName 属性名
     * @return {@link String}
     */
    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);
        }
    }

    /**
     * 通过两层for循环方式构建树
     *
     * @param list           列表
     * @param idName         id名称
     * @param parentIdName   父id名称
     * @param childrenName   子节点列表名称
     * @param topParentIdVal 顶层节点父id的值
     * @return {@link List}<{@link 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;
    }

}

三种方式对比

前两种方法的时间复杂度都和叶子节点的个数相关,我们假设叶子节点个数为m

方式一: 用递归的方法,时间复杂度等于:O(n +(n-m)* n),根据初始算法那篇文章的计算时间复杂度的方法,可以得到最终时间复杂度是O(n2)

方式二: 用两层嵌套循环的方法,时间复杂度等于:O(n +(n-m)* n),和方法一的时间复杂度是一样的,最终时间复杂度是O(n2)

方式三: 用两次遍历的方法,时间复杂度等于:O(3n),根据初始算法那篇文章的计算时间复杂度的方法,可以得到最终时间复杂度是O(n),但它的空间复杂度比前两种方法稍微大了一点,但是也是线性阶的,所以影响不是特别大。所以第三种方法是个人觉得比较优的一种方法文章来源地址https://www.toymoban.com/news/detail-821258.html

方式 时间复杂度
递归 平方阶,O(n2)
两层for循环 平方阶,O(n2)
Map 线性阶,O(n)

到了这里,关于java中把一个list转tree的三种方法——工具类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JavaScript删除字符串最后一个字符的三种方法

    JavaScript删除字符串最后一个字符的三种方法 在JavaScript中,我们经常需要操作字符串。有时候,我们可能需要删除字符串的最后一个字符。下面将介绍三种常见的方法来实现这个目标。 方法一:使用 slice 函数 slice 函数是JavaScript中一个常用的字符串方法,它可以返回一个新的

    2024年02月08日
    浏览(57)
  • Java延时的三种方法

    一、Robot,Thread和Timer 打印: 二、补充: 关于方法二的 this.cancel() ; 解释: 取消此计时器任务。如果任务已计划一次执行,但尚未运行,或尚未计划,则它将永远不会运行。如果任务已计划重复执行,则它将永远不会再次运行。(如果此调用发生时任务正在运行,则任务将运

    2024年02月16日
    浏览(45)
  • 使用java判断质数的三种方法

    方法一:质数只能被1和它本身整除  方法二:一个数总能写成“n = a * b”的形式,a和b之间一定有一个数不大于n/2  方法三:每一个整数都可以看做由两个数相乘得到,且每个乘数不大于原整数的平方根  

    2024年02月13日
    浏览(54)
  • Java 创建多线程的三种方法

    在Java中创建多线程,往往都要通过Thread类来实现,今天学习下Java中创建多线程的三种方法[1]。 通过继承 Thread类 实现多线程。 主要方法: 1.void run(), 线程开启后,方法将被调用执行 2.void start(), 使此线程开始执行, Java虚拟机会调用run()方法 实现步骤: 1.定义类,继承 Thread类

    2024年02月05日
    浏览(57)
  • java删除文件或目录的三种方法

    传统删除是利用IO流,本文利用NIO流实现。 代码如下(示例): 代码如下(示例): 代码如下(示例): 利用NIO流的好处: 1.如果删除失败,可以给出错误的具体原因; 2.代码不多,效率高。

    2024年02月10日
    浏览(65)
  • java中判断对象类型的三种方法

    instanceof instanceof 是 Java 中的一个,用于判断一个对象是否是指定类型或其子类型的实例。它的使用格式为: 其中, 对象 是待判断的对象, 类型 是要判断的类型。 instanceof 的返回值是一个布尔值,如果对象是指定类型或其子类型的实例,则返回 true ,否则返回

    2024年02月03日
    浏览(48)
  • java跳出for循环的三种常见方法

    这篇文章主要给大家介绍了关于java跳出for循环的三种常见方法,需要的朋友可以参考下 一、 break语句:使用break语句可以结束整个for循环的执行: 当 i 等于5时, break 语句会将控制流程跳出 for 循环从而停止后续代码的执行。 二、 return语句:如果你想要跳出当前方法并且停止

    2024年04月23日
    浏览(37)
  • JAVA——二维数组遍历二维数组的三种方法

    目录 🍒java中二维数组的定义和赋值 🍒二维数组遍历的三种方法 🍇第一种:for循环遍历 🍇第二种方法:通过Arrays.deepToString()遍历 🍇第三种方法:通过for(   :   )遍历 二维数组其实就是特殊的一维数组; 在java中将这句话诠释得淋漓尽致; 运行截图: 运行截图: 注意

    2024年02月08日
    浏览(42)
  • java获取系统CPU和内存使用率的三种方法

    获取系统CPU和内存的三种方法: 1、使用OperatingSystemMXBean获取 2、使用sigar方法获取 3、使用oshi方法获取 以下是我在我的机子上对三种方法测试的比较 方法    准确率 OperatingSystemMXBean    获取的内存数据准确,CPU差距有点大 sigar    获取的内存数据稍微有点差距,CPU相对

    2024年04月13日
    浏览(51)
  • java8 列表通过 stream流 根据对象属性去重的三种实现方法

    0、User对象 1、使用filter进行去重 测试 ①、疑惑 既然 filter 里面调用的是 distinctPredicate 方法,而该方法每次都 new 一个新的 map 对象,那么 map 就是新的,怎么能做到可以过滤呢 ②、解惑 先看一下 filter 的部分实现逻辑,他使用了函数式接口 Predicate ,每次调用filter时,会使用

    2024年01月20日
    浏览(108)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包