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

news/2024/10/6 4:39:59/文章来源:https://www.cnblogs.com/ningbeibei/p/18285757

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中所有的主键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);}}
}

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方式组装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; //子集
}

3.测试list转还tree的耗时

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/738751.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

博客屋网址导航自适应主题php源码

博客屋网址导航自适应主题php源码v1.0是一个以PHP+MySQL进行开发的网址导航源码。模板源码后台开源无加密,可二次开发,前端响应式自适应多端屏幕。主题源码适合个人建站技术,个人博客论坛,个人日记分享等个人网站内容。站长也可以修改成其他行业的内容目录导航。演示地址ht…

【vue】为什么v-for中需要一个key呢?

通过key值来区分循环中的子内容

在wsl中部署puppeteer的相关笔记

二. 缺少依赖问题反复提示缺少各种依赖,到处搜刮一顿操作之后是没问题了,但也不知道哪些是无所谓的 apt install -y gconf-service libc6 libcairo2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libstdc++6 libx11-6 apt inst…

《计算机组成与系统结构(第二版) 裘雪红 李伯成 西安电子科技大学出版社》课后习题答案(带解析)(七)

此系列答案配套《计算机组成与系统结构(第二版) 裘雪红 李伯成 西安电子科技大学出版社》一书相关内容。所有内容为博主个人编辑,仅作参考学习交流之用,转载请注明出处。如发现错误,请联系博主及时勘误。如有侵权行为,博主将立即下架全部内容。声明:此系列答案配套《计…

【设计模式(二)】创建型模式--抽象工厂模式

创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。抽象工厂模式也是⼀种创建型设计模式,提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 抽象工厂…

[Java] Java 关键字 : transient

0 序 Java中的transient关键字,transient是短暂的意思。对于transient修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。 因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。 1 序列化Java中对象的序列…

告别 .NET 7,支持将于 5 月结束——我们几乎不认识你

微软 .NET 7 软件框架的支持将于 5 月结束,这距离其 2022 年发布仅过去 18 个月——这提醒我们,长期更新时代正在成为过去。 .NET 7 于 2022 年 11 月 8 日首次亮相,与其前身不同的是,它是一个标准期限支持 (STS) 版本,这意味着它的支持期为 18 个月。.NET 6 和 .NET 8 都…

在Ubantu22.04中运行ORB_SLAM3

在Ubantu22.04中运行ORB_SLAM3 一、概述 ORB-SLAM3是一个支持视觉、视觉加惯导、混合地图的SLAM系统,可以在单目,双目和RGB-D相机上利用针孔或者鱼眼模型运行。从第一版的单目相机系统,到第二版加入了对stereo以及RGBD camera的支持,再到目前最新版本的orb-slam整合了visua…

gitlab 解锁账号

现象 登录gitlab后显示 账号被锁,登录方式AD域 管理员后台查看账号 如果是在gitlab导致的锁,那么账号显示的就是Blocked,如果是LDAP导致就是LDAP Blocked,并且后者无法在gitlab UI界面解锁 解锁登录控制端 gitlab-rails console搜索用户user = User.find_by_email("m…

centos 7 ip地址配置

然后输入如下命令: /etc/init.d/network restart我只想安静地学习,捡拾前人的牙慧,默默强大如此弱小的我...

手把手教你解决spring boot导入swagger2版本冲突问题,刘老师教编程

手把手教你解决spring boot导入swagger2版本冲突问题本文仅为个人理解,欢迎大家批评指错首先Spring Boot 3 和 Swagger 2 不兼容。在 Spring Boot 3 中,应该使用 Springdoc 或其他与 Spring Boot 3 兼容的 API 文档工具来替代 Swagger 2。 Swagger 2 的依赖底层使用的是 java…

Linux(Centos7)安装Docker 社区(ce)版

安装准备 查看Linux系统版本是否为centos7 cat /etc/os-release确保系统内核为3版本以上 uname -a安装Docker 如之前安装过请卸载 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker…