当前位置: 首页 > news >正文

java中把一个list转tree的方法

环境

我们有个需求,数据库要存一个无限级联的tree,比如菜单,目录,或者地区等数据,现有两个问题:

  1. 问如何设计表。
  2. 怎么返回给前端一个无线级联的json数据。

思考

第一个问题

在设计表的时候,我们保证每一条数据都有一个code,和parent表示code即可,就可以连成树tree。表设计如下:

CREATE TABLE `city_info`  (`code` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '行政区划代码',`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '名称',`type` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '类型:1-省;2-市;3-县/区',`short_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '简称',`parent` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '所属行政区划',`parents` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '所属行政区划分级'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '中国省市区县行政区域表' ROW_FORMAT = Dynamic;

第二个问题

我的想法是通过一个sql查询查出来所有数据,得到一个 list集合,然后就回到了主题,如何用java把list转tree。

准备环境

创建一个城市信息类

/*** 中国省市区县行政区域表* @TableName tbl_city_info*/
@TableName(value ="tbl_city_info")
@Data
public class CityInfo implements Serializable {/*** 主键id*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 行政区划代码*/@TableField(value = "code")private String code;/*** 名称*/@TableField(value = "name")private String name;/*** 类型:1-省;2-市;3-县/区*/@TableField(value = "type")private String type;/*** 简称*/@TableField(value = "short_name")private String shortName;/*** 所属行政区划*/@TableField(value = "parent")private String parent;/*** 所属行政区划分级*/@TableField(value = "parents")private String parents;/*** 所属行政区划分级* Mybatis-plus 映射字段时忽略字段*/@TableField(exist = false)private List<CityInfo> childCityInfo;@TableField(exist = false)private static final long serialVersionUID = 1L;
}

创建一个list转成tree的工具类:

public class TreeUtils{public static List<CityInfo> buildTree1(List<CityInfo> cityInfos) {// TODO : 第一种解法return null;}public static List<CityInfo> buildTree2(List<CityInfo> cityInfos) {// TODO : 第二种解法return null;}public static List<CityInfo> buildTree3(List<CityInfo> cityInfos) {// TODO : 第三种解法return null;}
}

第一种方法:递归

/*** 使用递归的方式* @param cityInfos 所有的数据* @return*/public List<CityInfo> buildTree1(List<CityInfo> cityInfos) {List<CityInfo> cityInfoList = new ArrayList<>(16);for (CityInfo cityInfo : cityInfos) {//找到根集合if (cityInfo.getParent().equals("0")) {cityInfoList.add(cityInfo);//添加子setChildren(cityInfos,cityInfo);}}return cityInfoList;}/*** 在所有集合数据中,找到所有的子结果* @param cityInfos 所以的数据* @param parent    需要找的这个城市的所有的子数据*/public void setChildren(List<CityInfo> cityInfos, CityInfo parent) {for (CityInfo cityInfo : cityInfos) {List<CityInfo> childCityInfo = parent.getChildCityInfo();//如果数据和要找的数据code和parent相等,说明找到了if (cityInfo.getParent().equals(parent.getCode())) {//如果是第一次,子结果是null,需要赋值一个空数组if (CollectionUtils.isEmpty(childCityInfo)) {childCityInfo = new ArrayList<>(16);}childCityInfo.add(cityInfo);parent.setChildCityInfo(childCityInfo);}}//如果上面遍历完了,而且子数据还是空,说明没有子数据,直接返回if (CollectionUtils.isEmpty(parent.getChildCityInfo())) {return;}//如果有子数据,需要找子数据,是否有子数据(就是找儿子的儿子)一直递归下去就行了for (CityInfo cityInfo : parent.getChildCityInfo()) {setChildren(cityInfos,cityInfo);}}

第二种方法:两层循环

 /*** 第二种方法:两层循环*      第一层遍历,是为找所有的根节点,*      第二层遍历,是为了找所有节点的,子节点*      *      这里只是返回所有的根节点,因为所有的节点的子节点都找到了,所以只要返回根节点,子节点自己就带上了* @param cityInfos 所有的数据* @return*/public List<CityInfo> buildTree2(List<CityInfo> cityInfos) {List<CityInfo> cityInfoList = new ArrayList<>(16);for (CityInfo cityInfo : cityInfos) {//找到根集合if (cityInfo.getParent().equals("0")) {cityInfoList.add(cityInfo);}//找到本次遍历的节点的,所有子节点(这里子节点就是一层)for (CityInfo child : cityInfos) {if (cityInfo.getCode().equals(child.getParent())) {List<CityInfo> childCityInfo = cityInfo.getChildCityInfo();if (CollectionUtils.isEmpty(childCityInfo)) {childCityInfo = new ArrayList<>(16);cityInfo.setChildCityInfo(childCityInfo);}childCityInfo.add(child);}}}return cityInfoList;}

第三种方法:两次遍历

/*** 第三种方法:两层循环*      第一层遍历,是为了找到所有父code下面的所有的子节点*      第二层遍历,是为了给所有的节点,设置子节点,并且找到所有根节点* @param cityInfos 所有的数据* @return*/public List<CityInfo> buildTree3(List<CityInfo> cityInfos) {Map<String, List<CityInfo>> cityInfoParentMap = new HashMap<>(16);//本次循环,是为了找到所有父code下面的所有的子节点for (CityInfo cityInfo : cityInfos) {String parent = cityInfo.getParent();List<CityInfo> children = cityInfoParentMap.getOrDefault(parent, new ArrayList<>());children.add(cityInfo);cityInfoParentMap.put(parent, children);}List<CityInfo> result = new ArrayList<>(16);//在次循环,是为了给所有的节点,设置子节点
//        for (CityInfo cityInfo : cityInfos) {
//            cityInfo.setChildCityInfo(cityInfoParentMap.get(cityInfo.getCode()));
//        }
//        //最好一次循环,是为了找到所有的根节点
//        for (CityInfo cityInfo : cityInfos) {
//            if (cityInfo.getParent().equals("0")) {
//                result.add(cityInfo);
//            }
//        }//第二次和第三次,可以合成一次循环for (CityInfo cityInfo : cityInfos) {cityInfo.setChildCityInfo(cityInfoParentMap.get(cityInfo.getCode()));if (cityInfo.getParent().equals("0")) {result.add(cityInfo);}}return result;}

第三种方法:两次遍历(使用Java8stream流)

  /*** 第三种方法:两层循环*      第一层遍历,是为了找到所有父code下面的所有的子节点*      第二层遍历,是为了给所有的节点,设置子节点,并且找到所有根节点* @param cityInfos 所有的数据* @return*/public List<CityInfo> buildTree3_stream(List<CityInfo> cityInfos) {//本次循环,是为了找到所有父code下面的所有的子节点Map<String, List<CityInfo>> cityInfoParenMap = cityInfos.stream().collect(Collectors.groupingBy(CityInfo::getParent));//本次循环,是为了给所有的节点,设置子节点cityInfos.forEach(cityInfo -> cityInfo.setChildCityInfo(cityInfoParenMap.get(cityInfo.getCode())));//是为了找到所有的根节点return cityInfos.stream().filter(cityInfo -> cityInfo.getParent().equals("0")).collect(Collectors.toList());}

注意:Collectors.groupingBy()方法使用。查看此链接:https://blog.csdn.net/qq_2662385590/article/details/132385605?spm=1001.2014.3001.5502

三种方法对比

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

  • 方法一: 用递归的方法,时间复杂度等于:O(n +(n-m)*
    n),根据初始算法那篇文章的计算时间复杂度的方法,可以得到最终时间复杂度是O(n2)
  • 方法二: 用两层嵌套循环的方法,时间复杂度等于:O(n +(n-m)* n),和方法一的时间复杂度是一样的,最终时间复杂度是O(n2)
  • 方法三:用两次遍历的方法,时间复杂度等于:O(3n),根据初始算法那篇文章的计算时间复杂度的方法,可以得到最终时间复杂度是O(n),但它的空间复杂度比前两种方法稍微大了一点,但是也是线性阶的,所以影响不是特别大。
  • 所以第三种方法是个人觉得比较优的一种方法
方法代码执行次数时间复杂度代码复杂程度
方法1O(n +(n-m)* n)平方阶,O(n2)一般
方法2O(n +(n-m)* n)平方阶,O(n2)良好
方法3O(3n)线性阶,O(n)复杂

相关文章:

java中把一个list转tree的方法

环境 我们有个需求&#xff0c;数据库要存一个无限级联的tree&#xff0c;比如菜单&#xff0c;目录&#xff0c;或者地区等数据&#xff0c;现有两个问题&#xff1a; 问如何设计表。怎么返回给前端一个无线级联的json数据。 思考 第一个问题 在设计表的时候&#xff0c;…...

QT设置widget背景图片

首先说方法&#xff0c;在给widget或者frame或者其他任何类型的控件添加背景图时&#xff0c;在样式表中加入如下代码&#xff0c;指定某个控件&#xff0c;设置其背景。 类名 # 控件名 { 填充方式&#xff1a;图片路径 } 例如&#xff1a; QWidget#Widget {border-image: url…...

【ROS】话题通信--从理论介绍到模型实现(C++)

1.简单介绍 话题通信是ROS中使用频率最高的一种通信模式&#xff0c;话题通信是基于发布订阅模式的&#xff0c;也即:一个节点发布消息&#xff0c;另一个节点订阅该消息。像雷达、摄像头、GPS… 等等一些传感器数据的采集&#xff0c;也都是使用了话题通信&#xff0c;换言之…...

服务器数据恢复-EqualLogic存储RAID5数据恢复案例

服务器数据恢复环境&#xff1a; 一台DELL EqualLogic存储中有一组由16块SAS硬盘组建的RAID5阵列。存储存放虚拟机文件&#xff0c;采用VMFS文件系统&#xff0c;划分了4个lun。 服务器故障&检测&分析&#xff1a; 存储设备上有两个硬盘指示灯显示黄色&#xff0c;存储…...

qsort函数详解

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解qsort函数&#xff0c;如果你觉得我写的不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 文章目录 一. qsort函数参数详解1.数组首元素地址base2.数组的元素个数num和元素所占内存空间大小w…...

C#学习,委托,事件,泛型,匿名方法

目录 委托 声明委托 实例化委托 委托的多播 委托的用途 事件 通过事件使用委托 声明事件 泛型 泛型的特性 泛型方法 泛型的委托 匿名方法 编写匿名方法的语法 委托 类似于指针&#xff0c;委托是存有对某个方法的引用的一种引用类型变量&#xff0c;引用可以在运…...

2023最新版本~KEIL5使用C++开发STM32

先看效果 开始教学 因为是第一次写这个配置教程 我会尽量详细些 打开一个Keil工程 移除本地core 添加在线core 第一次编译代码 不会有报错 修改main.c文件类型为C 点击魔术棒 把ARM编译器修改为V6 第二次编译会报错语法不兼容 我把汇编部分的这些代码做了…...

汽车领域专业术语

1. DMS/OMS/RMS/IMS DMS&#xff1a;即Driver Monitoring System&#xff0c;监测对象为Driver&#xff08;驾驶员&#xff09;。DMS三大核心&#xff1a; OMS&#xff1a;即Occupancy Monitoring System&#xff0c;监测对象为乘客。 RMS&#xff1a;后排盲区检测系统 IMS&…...

H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包

环境: H3C S6520-26Q-SI version 7.1.070, Release 6326 Win 10 专业版 Wireshake Version 4.0.3 问题描述: H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包 解决方案: 配置交换机本地端口镜像 1.进入系统视图,并创建本地镜像组1 <H3C>system-vie…...

零基础自学:2023 年的今天,请谨慎进入网络安全行业

前言 2023 年的今天&#xff0c;慎重进入网安行业吧&#xff0c;目前来说信息安全方向的就业对于学历的容忍度比软件开发要大得多&#xff0c;还有很多高中被挖过来的大佬。 理由很简单&#xff0c;目前来说&#xff0c;信息安全的圈子人少&#xff0c;985、211 院校很多都才…...

向gitee推送代码

目录 一、Gitee创建仓库 二、将刚刚创建的仓库放到虚拟机上 2.1 https 方式克隆仓库 2.2 ssh的方式克隆仓库 三、本地开发&#xff0c;推送 3.1 查看是否有远程库 3.2 推送代码 3.3 查看是否推送成功 一、Gitee创建仓库 二、将刚刚创建的仓库放到虚拟机上 2.1 https 方式…...

双指针算法实例1(移动零)

常⻅的双指针有两种形式&#xff1a; 1 对撞指针&#xff08;左右指针&#xff09;&#xff1a; a 对撞指针从两端向中间移动。⼀个指针从最左端开始&#xff0c;另⼀个从最右端开始&#xff0c;然后逐渐往中间逼 近 b 终止条件一般是两指针相遇or错过&#xff08;也可能在循…...

C#程序随系统启动例子 - 开源研究系列文章

今天讲讲C#中应用程序随系统启动的例子。 我们知道&#xff0c;应用程序随系统启动&#xff0c;都是直接在操作系统注册表中写入程序的启动参数&#xff0c;这样操作系统在启动的时候就根据启动参数来启动应用程序&#xff0c;而我们要做的就是将程序启动参数写入注册表即可。此…...

最全攻略之人工智能顶会论文发表

最全攻略之人工智能顶会论文发表 1. 人工智能顶会1.1 CCF 顶会列表2023年人工智能顶会时间线 2.人工智能顶会论文发表流程2.1 顶会论文发表流程2.2 顶会论文审稿流程 3.1顶会论文发表指南3.1 顶会论文七要素3.2 顶会论文写作要点 4.人工智能发展趋势4.1 人工智能未来趋势4.2 人…...

Redis基于内存的key-value结构化NOSQL(非关系型)数据库

Redis Redis介绍Redis的优点Redis的缺点Redis的安装Redis的连接Redis的使用Redis中的数据类型String的使用get setsetex(expire)ttlsetnx(not exit)HashList列表(队列)Set集合ZSet集合Redis 通用命令Redis图形客户端Redis在Java中的使用RedisTemplate...

Spring学习笔记+SpringMvc+SpringBoot学习笔记

壹、核心概念&#xff1a; 1.1. IOC和DI IOC&#xff08;Inversion of Control&#xff09;控制反转&#xff1a;对象的创建控制权由程序转移到外部&#xff0c;这种思想称为控制反转。/使用对象时&#xff0c;由主动new产生对象转换为由外部提供对象&#xff0c;此过程种对象…...

如何在 3Ds Max 中准确地将参考图像调整为正确的尺寸?

您是否想知道如何在 3Ds Max 中轻松直观地调整参考图像的大小&#xff0c;而无需借助第三方解决方案、插件或脚本&#xff1f; 我问自己这个问题&#xff0c;并高兴地发现了FFD Box 2x2x2&#xff0c;我无法停止钦佩这个修改器的多功能性。 在本文中&#xff0c;我想与您分享一…...

集简云推出的全国第一款 AI+连接器解决方案产品语聚AI

语聚AI是集简云推出的全国第一款 AI连接器解决方案产品&#xff0c;官网&#xff1a;https://yuju.jijyun.cn 语聚AI包括了多个不同的AI功能&#xff0c;协助企业和个人更好的使用AI语言模型所带来的能力&#xff0c;包括&#xff1a; 应用助手 希望通过AI智能助手帮助您查询C…...

git错误记录

露id没有影响&#xff0c;搞得微软不知道我ip一样 git fatal: 拒绝合并无关的历史的错误解决(亲测有效)...

linux使用jmeter进行压测

1.准备好服务器&#xff0c;这里默认服务器用的系统镜像为contos7.9.2009 2.准备好jmeter的测试计划文件 .jmx 这里默认测试计划的jmx文件在 /nas目录下 3.安装JDK与jmeter进行测试 #创建JDK与jmeter目录&#xff0c;并复制安装文件 mkdir /jmeter mkdir /jmeter/jav…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...