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

news2024/11/19 1:26:54

目录

  • 前言
  • 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;


	@Override
	public 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;

	@Override
	public 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;
}

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

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

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

	@Override
	public 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.coloradmin.cn/o/1413370.html

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

相关文章

BLIP-2: 基于冻结图像编码器和大型语言模型的语言-图像预训练引导

BLIP-2: 基于冻结图像编码器和大型语言模型的语言-图像预训练引导 项目地址BLIP-2的背景与意义BLIP-2的安装与演示BLIP-2模型库图像到文本生成示例特征提取示例图像-文本匹配示例性能评估与训练引用BLIP-2Hugging Face集成 在语言-图像预训练领域&#xff0c;BLIP-2的出现标志着…

【图像分割】【深度学习】Windows10下UNet代码Pytorch实现与源码讲解

【图像分割】【深度学习】Windows10下UNet代码Pytorch实现与源码讲解 提示:最近开始在【医学图像分割】方面进行研究,记录相关知识点,分享学习中遇到的问题已经解决的方法。 文章目录 【图像分割】【深度学习】Windows10下UNet代码Pytorch实现与源码讲解前言UNet模型运行环境搭…

【运行Python爬虫脚本示例】

主要内容&#xff1a;Python中的两个库的使用。 1、requests库&#xff1a;访问和获取网页内容&#xff0c; 2、beautifulsoup4库&#xff1a;解析网页内容。 一 python 爬取数据 1 使用requests库发送GET请求&#xff0c;并使用text属性获取网页内容。 然后可以对获取的网页…

数仓治理-数据表合规治理

注&#xff1a;文章参考:数据治理实践 | 数据表合规治理本期将从数据表治理角度出发&#xff0c;探讨数据表合规治理的最佳实践及相关挑战https://mp.weixin.qq.com/s/5ImY5niYNOb_VpicUcasCg 目录 前言 一、数据表合规治理的背景 二、数据表合规治理前的思考 三、数据表合…

c# cad2016选择封闭多段线获取多段线面积

在C#中&#xff0c;如果你想要通过AutoCAD .NET API来选择封闭多段线内部的其他闭合多段线并计算它们各自的面积&#xff0c;可以遵循以下基本步骤&#xff1a; 1、加载AutoCAD库&#xff1a; 确保你的C#项目引用了Autodesk.AutoCAD.Interop和Autodesk.AutoCAD.Interop.Common…

ERROR Failed to get response from https://registry.npm.taobao.org/ 错误的解决

这个问题最近才出现的。可能跟淘宝镜像的证书到期有关。 解决方式一&#xff1a;更新淘宝镜像&#xff08;本人测试无效&#xff0c;但建议尝试&#xff09; 虽然无效&#xff0c;但感觉是有很大关系的。还是设置一下比较好。 淘宝镜像的地址&#xff08;registry.npm.taobao…

燃烧的指针(二)

&#x1f308;个人主页&#xff1a;小田爱学编程 &#x1f525; 系列专栏&#xff1a;c语言从基础到进阶 &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于c语言的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎来到小田代码世界~ &#x…

MacBook自带邮箱设置

MacBook自带邮箱设置 邮件—->偏好设置 服务器设置 收件服务器(POP) 用户名: xxxxxxliang 密码: ***** 主机名:mail.xxx.com.cn 自动管理连接设置 勾上 发件服务器(SMTP) 帐户:xxx.com.cn 用户名:xxxxxxliang 密码:**** 主机名:mail.xxx.com.cn 注意: 自动管理连接设置 不…

transformer和vit学习笔记

以下记录自己对transformer的学习笔记&#xff0c;可能自己看得懂【久了自己也忘了看不懂】&#xff0c;别人看起来有点乱。以后再优化文档~ 小伙伴请直接去看学习资源&#xff1a; Transformer的理解T-1_哔哩哔哩_bilibili 首先&#xff0c;时序处理&#xff1a;一些模型的出…

go 引用fork后的模块的两种方式(replace和工作区)

很久没更新了&#xff0c;一是工作琐碎&#xff0c;二是处在舒适区&#xff0c;但最近看着身边的同事一个个离开&#xff0c;危机感骤然而生&#xff0c;不得不重拾书本&#xff0c;毕竟生活还得继续&#xff0c;不卷是不可能的&#xff0c;谁让我们生在这个卷中卷的国度&#…

组件冲突、data函数、组件通信

文章目录 1.组件的三大组成部分 - 注意点说明2.组件的样式冲突&#xff08;用 scoped 解决&#xff09;3.data是一个函数4.组件通信1.什么是组件通信&#xff1f;2.不同的组件关系 和 组件通信方案分类 5.prop详解prop 校验①类型校验②完整写法&#xff08;类型&#xff0c;非…

QtRVSim(二)一个 RISC-V 程序的解码流程

继上一篇文章简单代码分析后&#xff0c;本文主要调研如何实现对指令的解析运行。 调试配置 使用 gdb 工具跟踪调试运行。 c_cpp_properties.json 项目配置&#xff1a; {"name": "QtRvSim","includePath": ["${workspaceFolder}/**&quo…

如何在Shopee平台上进行家居类目的选品

在Shopee平台上进行家居类目的选品是卖家们提高销售业绩和市场竞争力的重要步骤。通过深入了解市场趋势、竞争对手、消费者偏好和供应链等方面的信息&#xff0c;卖家可以制定有效的选品策略。本文将介绍一些在Shopee平台上进行家居类目选品时的策略和注意事项。 先给大家推荐…

分布式因果推断在美团履约平台的探索与实践

美团履约平台技术部在因果推断领域持续的探索和实践中&#xff0c;自研了一系列分布式的工具。本文重点介绍了分布式因果树算法的实现&#xff0c;并系统地阐述如何设计实现一种分布式因果树算法&#xff0c;以及因果效应评估方面qini_curve/qini_score的不足与应对技巧。希望能…

pytest参数化

一、pytest.mark.parametrize介绍 pytest.mark.parametrize(argnames, argvalues, indirectFalse, idsNone)参数说明&#xff1a; argnames: 一个或多个参数名&#xff0c;用逗号分隔的字符串&#xff0c;如"arg1,arg2,arg3"&#xff0c;参数名与用例入参数一致。 a…

Flink问题解决及性能调优-【Flink根据不同场景状态后端使用调优】

Flink 实时groupby聚合场景操作时&#xff0c;由于使用的是rocksdb状态后端&#xff0c;发现CPU的高负载卡在rocksdb的读写上&#xff0c;导致上游算子背压特别大。通过调优使用hashmap状态后端代替rocksdb状态后端&#xff0c;使吞吐量有了质的飞跃&#xff08;20倍的性能提升…

Rabbitmq调用FeignClient接口失败

文章目录 一、框架及逻辑介绍1.背景服务介绍2.问题逻辑介绍 二、代码1.A服务2.B服务3.C服务 三、解决思路1.确认B调用C服务接口是否能正常调通2.确认B服务是否能正常调用A服务3.确认消息能否正常消费4.总结 四、修改代码验证1.B服务异步调用C服务接口——失败2.将消费消息放到C…

【Web】CTFSHOW SQL注入刷题记录(上)

目录 无过滤注入 web171 web172 web173 web174 web175 时间盲注 写马 过滤注入 web176 web177 web178 web179 web180 web181-182 web183 web184 web185-186 web187 web188 web189 web190 布尔盲注 web191 web192 web193 web194 堆叠注入 web195 …

对于gzip的了解

gzip基本操作原理&#xff1a;通过消除文件中的冗余信息&#xff0c;使用哈夫曼编码等算法&#xff0c;将文件体积压缩到最小。这种数据压缩方式在网络传输中发挥了巨大作用&#xff0c;减小了传输数据的大小&#xff0c;从而提高了网页加载速度。 静态资源 Vue Vue CLl修改v…

Task04:DDPG、TD3算法

本篇博客是本人参加Datawhale组队学习第四次任务的笔记 【教程地址】https://github.com/datawhalechina/joyrl-book 【强化学习库JoyRL】https://github.com/datawhalechina/joyrl/tree/main 【JoyRL开发周报】 https://datawhale.feishu.cn/docx/OM8fdsNl0o5omoxB5nXcyzsInGe…