详细分析Java的树形工具类(含注释)

目录

  • 前言
  • 1. 基本框架
  • 2. 实战应用

前言

对应的每个子孙属于该父亲,这其实是数据结构的基础知识,那怎么划分怎么归属呢

对应的基本知识推荐如下:

  1. 【数据结构】树和二叉树详细分析(全)
  2. 【数据结构】B树和B+树的笔记详细诠释

1. 基本框架

最基本的树形结构节点,一个自身ID,一个父亲ID,一个孩子ID:

import java.io.Serializable;
import java.util.List;public interface INode<T> extends Serializable {Long getId();Long getParentId();List<T> getChildren();default Boolean getHasChildren() {return false;}
}

其中ForestNodeManager 树形管理类主要表示如下:

  • nodeMap 存储节点,并提供了一些方法来操作节点和管理树状结构
  • getRoot 方法用于获取合并后的树的根节点列表
  • addParentId 方法用于标记尚未创建的父节点
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class ForestNodeManager<T extends INode<T>> {/*** 使用 ImmutableMap 存储节点,其中键是节点的ID,值是节点本身。* 这个映射是不可变的,通过调用 Maps.uniqueIndex(nodes, INode::getId) 来创建,其中 INode::getId 是获取节点ID的方法。* * parentIdMap: 使用 HashMap 存储尚未创建的父节点的ID。键是父节点的ID,值是一个占位对象(在这里是空字符串)。*/private final ImmutableMap<Long, T> nodeMap;private final Map<Long, Object> parentIdMap = Maps.newHashMap();/*** 接受一个 List<T> 类型的参数 nodes,并使用 Maps.uniqueIndex 方法将其转换为 ImmutableMap 存储在 nodeMap 中*/public ForestNodeManager(List<T> nodes) {this.nodeMap = Maps.uniqueIndex(nodes, INode::getId);}/*** 接受一个 Long 类型的节点ID作为参数,然后尝试从 nodeMap 中获取对应ID的节点。* 如果存在该节点,则返回节点;否则返回 null。*/public INode<T> getTreeNodeAt(Long id) {return this.nodeMap.containsKey(id) ? (INode)this.nodeMap.get(id) : null;}/*** 接受一个 Long 类型的父节点ID作为参数,将其添加到 parentIdMap 中。* 这个方法用于标记尚未创建的父节点。*/public void addParentId(Long parentId) {this.parentIdMap.put(parentId, "");}/*** 创建一个空的 ArrayList 用于存储树的根节点。* 使用 forEach 遍历 nodeMap 中的每个节点。* 对于每个节点,如果其父节点ID为0(表示是根节点)或者父节点ID在 parentIdMap 中存在(即尚未创建的父节点),则将该节点添加到根节点列表中。* 最后返回根节点列表。*/public List<T> getRoot() {List<T> roots = new ArrayList();this.nodeMap.forEach((key, node) -> {if (node.getParentId() == 0L || this.parentIdMap.containsKey(node.getId())) {roots.add(node);}});return roots;}
}

对于ForestNodeMerger合并类:

包含树状结构节点的列表,根据节点之间的父子关系进行合并,最终返回合并后的树的根节点列表。

import java.util.List;public class ForestNodeMerger {public ForestNodeMerger() {}/*** 对于每个节点,通过 getParentId() 方法获取其父节点的ID。* 如果父节点ID不等于0(即有父节点),则尝试通过 forestNodeManager.getTreeNodeAt() 方法获取父节点。* 如果成功获取到父节点,则将当前节点添加到父节点的子节点列表中。否则,说明父节点尚未被创建,需要通过 forestNodeManager.addParentId() 方法添加到待创建父节点的列表中。* * 最后,通过 forestNodeManager.getRoot() 方法获取合并后的树的根节点列表,并返回这个列表。*/public static <T extends INode<T>> List<T> merge(List<T> items) {ForestNodeManager<T> forestNodeManager = new ForestNodeManager(items);items.forEach((forestNode) -> {if (forestNode.getParentId() != 0L) {INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());if (node != null) {node.getChildren().add(forestNode);} else {forestNodeManager.addParentId(forestNode.getId());}}});return forestNodeManager.getRoot();}
}

2. 实战应用

大多数本身Mapper 或者 Service基层都会帮我们实现增删改查,主要是XML文件对数据库的逻辑

本身就是MybatisPlus框架,推荐阅读:

  1. Springboot整合MybatisPlus的基本CRUD(全)
  2. MyBatis-plus从入门到精通(全)

与日常开发差不多,主要是上述中的方法如何套用!


假设要做一个菜单专栏,必须由这种树形结构来管理,好方便那个子类归属那个父类!

在这里插入图片描述
对应的类别如下:

@Data
@TableName("menu")
@ApiModel(value = "Menu对象", description = "Menu对象")
public class Menu implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/@JsonSerialize(using = ToStringSerializer.class)@ApiModelProperty(value = "主键")@TableId(value = "id", type = IdType.ASSIGN_ID)private Long id;/*** 菜单父主键*/@JsonSerialize(using = ToStringSerializer.class)@ApiModelProperty(value = "菜单父主键")private Long parentId;/*** 菜单编号*/@ApiModelProperty(value = "菜单编号")private String code;/*** 菜单名称*/@ApiModelProperty(value = "菜单名称")private String name;/*** 菜单别名*/@ApiModelProperty(value = "菜单别名")private String alias;/*** 请求地址*/@ApiModelProperty(value = "请求地址")private String path;/*** 菜单资源*/@ApiModelProperty(value = "菜单资源")private String source;/*** 排序*/@ApiModelProperty(value = "排序")private Integer sort;/*** 菜单类型*/@ApiModelProperty(value = "菜单类型")private Integer category;/*** 操作按钮类型*/@ApiModelProperty(value = "操作按钮类型")private Integer action;/*** 是否打开新页面*/@ApiModelProperty(value = "是否打开新页面")private Integer isOpen;/*** 备注*/@ApiModelProperty(value = "备注")private String remark;/*** 是否已删除*/@TableLogic@ApiModelProperty(value = "是否已删除")private Integer isDeleted;@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (obj == null) {return false;}Menu other = (Menu) obj;if (Func.equals(this.getId(), other.getId())) {return true;}return false;}}

对应与前端交互的VO类如下:public class MenuVO extends Menu implements INode<MenuVO>,大部分这里都是这种写法!

@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "MenuVO对象", description = "MenuVO对象")
public class MenuVO extends Menu implements INode<MenuVO> {private static final long serialVersionUID = 1L;/*** 主键ID*/@JsonSerialize(using = ToStringSerializer.class)private Long id;/*** 父节点ID*/@JsonSerialize(using = ToStringSerializer.class)private Long parentId;/*** 子孙节点*/@JsonInclude(JsonInclude.Include.NON_EMPTY)private List<MenuVO> children;/*** 是否有子孙节点*/@JsonInclude(JsonInclude.Include.NON_EMPTY)private Boolean hasChildren;@Overridepublic List<MenuVO> getChildren() {if (this.children == null) {this.children = new ArrayList<>();}return this.children;}/*** 上级菜单*/private String parentName;/*** 菜单类型*/private String categoryName;/*** 按钮功能*/private String actionName;/*** 是否新窗口打开*/private String isOpenName;
}

对应的懒加载实现类主要如下:

	@Overridepublic List<MenuVO> lazyList(Long parentId, Map<String, Object> param) {if (Func.isEmpty(Func.toStr(param.get("parentId")))) {parentId = null;}return baseMapper.lazyList(parentId, param);}

对应获取树形结构的实现类如下:

	@Overridepublic List<MenuVO> tree() {return ForestNodeMerger.merge(baseMapper.tree());}

其中涉及Mapper的方法主要在xml文件中实现:

<select id="tree" resultMap="treeNodeResultMap">select id, parent_id, name as title, id as "value", id as "key" from menu where is_deleted = 0 and category = 1
</select>

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

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

相关文章

数据结构与算法教程,数据结构C语言版教程!(第六部分、数据结构树,树存储结构详解)二

第六部分、数据结构树&#xff0c;树存储结构详解 数据结构的树存储结构&#xff0c;常用于存储逻辑关系为 "一对多" 的数据。 树存储结构中&#xff0c;最常用的还是二叉树&#xff0c;本章就二叉树的存储结构、二叉树的前序、中序、后序以及层次遍历、线索二叉树、…

Docker部署思维导图工具SimpleMindMap并实现公网远程访问

文章目录 1. Docker一键部署思维导图2. 本地访问测试3. Linux安装Cpolar4. 配置公网地址5. 远程访问思维导图6. 固定Cpolar公网地址7. 固定地址访问 SimpleMindMap 是一个可私有部署的web思维导图工具。它提供了丰富的功能和特性&#xff0c;包含插件化架构、多种结构类型&…

【Unicode】Character ‘ENQUIRY‘ (U+0005)

询问 result.append("\u0005");Unicode Character ‘ENQUIRY’ (U0005)

js中字符串string,遍历json/Object【匹配url、邮箱、电话,版本号,千位分割,判断回文】

目录 正则 合法的URL 邮箱、电话 字符串方法 千位分割&#xff1a;num.slice(render, len).match(/\d{3}/g).join(,) 版本号比较 判断回文 json/Object 遍历 自身属性 for...inhasOwnProperty(key) Object.获取数组(obj)&#xff1a;Object.keys&#xff0c;Object…

深度强化学习(王树森)笔记02

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…

taro3 + vue3 + ts 跨平台体验记录

taro3 vue3 ts 跨平台体验记录&#xff0c;根据进度不定期更新。 目标平台包含&#xff1a;H5、微信小程序、APP。开发环境&#xff1a;windows 安装cli【官方安装文档】 npm install -g tarojs/cli常用命令 // 查看taro版本 npm info tarojs/cli创建demo项目 taro init…

spring-boot redis stream消息队列demo-及死信简单处理

Redis stream 是 Redis 5 引入的一种新的数据结构&#xff0c;它是一个高性能、高可靠性的消息队列&#xff0c;主要用于异步消息处理和流式数据处理。在此之前&#xff0c;想要使用 Redis 实现消息队列&#xff0c;通常可以使用例如&#xff1a;列表&#xff0c;有序集合、发布…

【C++】istream类型对象转换为逻辑条件判断值

前言 大家好吖&#xff0c;欢迎来到 YY 滴 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》专…

LiveGBS流媒体平台GB/T28181常见问题-如何快速查看推流上来的摄像头并停止摄像头推流?

LiveGBS流媒体平台GB/T28181常见问题-如何快速查看推流上来的摄像头并停止摄像头推流&#xff1f; 1、负载信息2、负载信息说明3、会话列表查看3.1、会话列表 4、停止会话5、搭建GB28181视频直播平台 1、负载信息 实时展示直播、回放、播放、录像、H265、级联等使用数目 2、负…

Python算法题集_接雨水

本文为Python算法题集之一的代码示例 题目42&#xff1a;接雨水 说明&#xff1a;给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]…

ElasticSearch7.7.1集群搭建

前言 Elasticsearch&#xff08;ES&#xff09;是一个基于Apache Lucene的分布式、高扩展、近实时的搜索引擎&#xff0c;主要用于海量数据快速存储、实时检索、高效分析的场景。通过简单易用的RESTful API&#xff0c;Elasticsearch隐藏了Lucene的复杂性&#xff0c;使得全文搜…

数论Leetcode204. 计数质数、Leetcode858. 镜面反射、Leetcode952. 按公因数计算最大组件大小

Leetcode204. 计数质数 题目 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 代码 class Solution:def countPrimes(self, n: int) -> int:if n < 2:return 0prime_arr [1 for _ in range(n)]prime_arr[0], prime_arr[1] 0, 0ls list()for i in…

Python编程 从入门到实践(项目二:数据可视化)

本篇为实践项目二&#xff1a;数据可视化。 配合文章python编程入门学习&#xff0c;代码附文末。 项目二&#xff1a;数据可视化 1.生成数据1.1 安装Matplotlib1.2 绘制简单的折线图1.2.1 修改标签文字和线条粗细1.2.2 校正图形1.2.3 使用内置样式1.2.4 使用scatter()绘制散点…

点云格式-PCD格式介绍

PCD格式介绍 一、概述二、PCD 版本三、文件格式头信息四、数据存储格式类型五、优于其他文件格式的优点六、例子 一、概述 PCD文件格式是PCL库最常用的一种数据格式、也是其提供的一个独有的数据格式&#xff0c;PCD文件格式并不是要重新发明轮子&#xff0c;而是为了补充现有的…

59.螺旋矩阵II(力扣LeetCode)

59.螺旋矩阵II 题目描述 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 示例 2&#xff1a; 输…

【测试】测试用例场景设计

专注 文章目录 用例设计公式登录测试用例测试用例设计思路购物车测试用例水杯设计测试用例发红包测试用例1. 正常情况下的测试用例&#xff1a;2. 边界情况下的测试用例&#xff1a;3. 异常情况下的测试用例&#xff1a;4. 特殊情况下的测试用例&#xff1a; 微信朋友圈1. 正常…

Django模型(一)

一、介绍 模型,就是python中的类对应数据库中的表 1.1、ORM ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写 ORM 把数据库映射成对象 1.2、示例 1.2.1、模型 from django.db import models…

字节跳动员工:5年攒了8400股,价值940W,财富自由的味道

字节跳动员工&#xff1a;5年攒了8400股&#xff0c;价值940W&#xff0c;财富自由的味道 最近&#xff0c;一位字节跳动员工在网络上爆料了他的财富增长故事&#xff0c;引起了广泛关注。这位员工在贴文中自豪地宣布&#xff0c;他加入字节跳动的五年间从未卖出手中的股票&am…

Ultraleap 3Di新建项目之给所有的Joint挂载物体

工程文件 Ultraleap 3Di给所有的Joint挂载物体 前期准备 参考上一期文章&#xff0c;进行正确配置 Ultraleap 3Di配置以及在 Unity 中使用 Ultraleap 3Di手部跟踪 新建项目 初始项目如下&#xff1a; 新建Create Empty 将新建的Create Empty&#xff0c;重命名为LeapPro…

查询redis路径,清除redis缓存

查询redis路径 1、执行ps -ef | grep redis 命令&#xff0c;结果如下&#xff08;记住PID&#xff09; 2、执行ps -u 系统用户名&#xff0c;进一步确定进程id, 我这里的系统用户名是root&#xff0c;执行ps -u root&#xff0c;结果如下&#xff1a; 结合1的操作结果图可知…