当前位置: 首页 > 编程日记 > 正文

使用树形结构保存实体

阅读原文请访问我的博客BrightLoong's Blog

之前在项目需要实现一个功能——将xml文件映射成实体,然后对映射的实体进行逻辑处理,最后保存到数据库中;由于xml结构的数据是结构化的数据,所以需要保证保存的数据具有正确的主外键关联。如下所示,是一个需要保存到数据库的xml文件。当映射成对应的实体school和student的时候,我们需要知道“school-one”下面有哪些学生,“school-two”下面有哪些学生,这个时候想到了使用树形结构来保存实体,让实体之间依然存在关联关系。

<school-inf><msg>2017-10-1XX省学校信息总汇</msg><schools><school><name>school-one</name><students><student>Jack</student><student>Rose</student><student>Jon</student></students></school><school><name>school-two</name><students><student>Bob</student><student>Alisa</student></students></school></schools>
</school-inf>
复制代码

树形工具

以下是树形工具类的实现,包含了树形节点类和树形结构类,由于代码中注释已经比较全面,所以不做过多的说明。

树形节点类BeanTreeNode.java

每一个节点对应一个实体,节点包含了实体信息,为了保证实体之间的关联关系,需要留有父节点信息,所有的子节点信息。由此推断出,节点的主要成员有

  • 父节点信息
  • 所有子节点信息
  • 当前实体信息

为了方便操作,我还多增加了id和pid(parent id),以及节点类型(nodeType)。对id的相关操作我并没有添加,如果需要可以自行添加。

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;/*** 实体树形结构点* BeanTreeNode* @author BrightLoong* @version 1.0**/
public class BeanTreeNode {/**标识id*/private String id;/**父id标识,为了方便获取冗余出来*/private String pid;/**父节点*/private BeanTreeNode parentNode;/**节点类型*/private String nodeType;/**节点值*/private Object bean;/**子节点*/private List<BeanTreeNode> childNodes;/*** @param parentNode* @param nodeType* @param bean* @param childNodes*/public BeanTreeNode(BeanTreeNode parentNode, String nodeType, Object bean) {this.parentNode = parentNode;this.nodeType = nodeType;this.bean = bean;this.childNodes = new ArrayList<BeanTreeNode>();this.id = UUID.randomUUID().toString().replaceAll("-", "");if (parentNode != null) {this.pid = parentNode.getId();}}/*** @return the nodeType*/public String getNodeType() {return nodeType;}/*** @param nodeType the nodeType to set*/public void setNodeType(String nodeType) {this.nodeType = nodeType;}/*** @return the parentNode*/public BeanTreeNode getParentNode() {return parentNode;}/*** @param parentNode the parentNode to set*/public void setParentNode(BeanTreeNode parentNode) {this.parentNode = parentNode;}/*** @return the bean*/public Object getBean() {return bean;}/*** @param bean the bean to set*/public void setBean(Object bean) {this.bean = bean;}/*** @return the childNodes*/public List<BeanTreeNode> getChildNodes() {return childNodes;}/*** @param childNodes the childNodes to set*/public void setChildNodes(List<BeanTreeNode> childNodes) {this.childNodes = childNodes;}/*** @return the id*/public String getId() {return id;}/*** @param id the id to set*/public void setId(String id) {this.id = id;}/*** @return the pid*/public String getPid() {return pid;}/*** @param pid the pid to set*/public void setPid(String pid) {this.pid = pid;}/*** 是否具有子节点* @return true or false*/public boolean haveChild() {return !CollectionUtils.isEmpty(childNodes);}
}
复制代码

树形结构类BeanTree.java

BeanTree.java里面包含了如下的一些常用操作:

  • 返回根节点
  • 返回最后添加节点
  • 判断是否具有子节点
  • 添加节点
  • 移动节点到其他节点下
  • 获取对应nodeType的所有节点或实体
  • 根据实体获取节点
  • 获取父节点
  • 转化为map结构

代码如下

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.commons.collections.CollectionUtils;/*** 实体树形结构* BeanTree* @author BrightLoong* @version 1.0**/
public class BeanTree {/**根节点*/private BeanTreeNode root;/*** 最新添加的节点*/private BeanTreeNode currentNode;/*** @return the currentNode*/public BeanTreeNode getCurrentNode() {return currentNode;}/*** @return the root*/public BeanTreeNode getRoot() {return root;}/*** 判断节点是否有子节点.* @param node 要判断的节点* @return true or false*/public boolean haveChild(BeanTreeNode node) {return CollectionUtils.isEmpty(node.getChildNodes());}/*** 在父节点上面添加节点,并返回天添加的节点.* @param parentNode 父节点* @param bean 要添加的bean* @param nodeType 节点类型* @return 返回包含bean的节点*/public BeanTreeNode addNode(BeanTreeNode parentNode, Object bean, String nodeType) {BeanTreeNode node;if (bean == null) {return null;}//如果没有父节点说明为root根节点if (parentNode == null) {node = root = new BeanTreeNode(null, nodeType, bean);} else {//创建子节点,并添加到父节点上node = new BeanTreeNode(parentNode, nodeType, bean);parentNode.getChildNodes().add(node);}currentNode = node;return node;}/*** 将当期bean-sBean,以及sBean下的子Bean,挂到dBean下* @param sBean 源Bean* @param dBean 目的父Bean*/public void removeTo(Object sBean, Object dBean) {BeanTreeNode sNode = getNodeByBean(sBean);BeanTreeNode dNode = getNodeByBean(dBean);removeTo(sNode, dNode);}/*** 将当期node-sNode,以及sNode下的子Node,挂到dNode下* @param sNode 源node* @param dNode 目的父node*/public void removeTo(BeanTreeNode sNode, BeanTreeNode dNode) {//从当前父节点移除sNodesNode.getParentNode().getChildNodes().remove(sNode);//将sNode移到dNode下dNode.getChildNodes().add(sNode);//修改sNode的父Id和父节点sNode.setPid(dNode.getId());sNode.setParentNode(dNode);}/*** 获取父bean.* @param bean 子bean* @return 返回父bean*/public Object getParentBean(Object bean) {return getNodeByBean(bean).getParentNode().getBean();}/*** 根据传入的bean获取bean下面对应类型的子bean.* @param bean 当前bean* @param nodeType 节点类型* @return 子bean的集合*/public List<Object> getBeanListByBeanAndNodeType(Object bean, String nodeType) {BeanTreeNode node = getNodeByBean(bean);return getBeanListByNodeType(node, nodeType);}/*** 根据传入的bean获取包含bean的Node节点* @param node 当前node* @param bean 要查找的bean* @return node节点*/public BeanTreeNode getNodeByBean(BeanTreeNode node, Object bean) {BeanTreeNode resultNode = null;if (node.getBean().equals(bean)) {resultNode = node;return resultNode;} else {for (BeanTreeNode tempNode : node.getChildNodes()) {resultNode = getNodeByBean(tempNode, bean);if (resultNode != null) {break;}}}return resultNode;}/*** 根据传入的bean获取root节点下包含bean的Node节点* @param bean 要查找的bean* @return node节点*/public BeanTreeNode getNodeByBean(Object bean) {return getNodeByBean(root, bean);}/*** 根据节点类型返回当前节点下对应节点类型的bean的list集合.* 默认如果当前节点满足类型,那么当前节点不会存在相同类型的子节点* @param node 当前节点* @param nodeType 节点类型* @return*/@SuppressWarnings("unchecked")public <T> List<T> getBeanListByNodeType(BeanTreeNode node, String nodeType) {List<T> beanList = new ArrayList<T>();if (node.getNodeType().equals(nodeType)) {beanList.add((T)node.getBean());} else {for (BeanTreeNode tempNode : node.getChildNodes()) {beanList.addAll((Collection<? extends T>) getBeanListByNodeType(tempNode, nodeType));}}return beanList;}/*** 根据节点类型返回根节点下对应节点类型的bean的list集合.* @param nodeType 节点类型* @return*/public <T> List<T> getBeanListByNodeType(String nodeType) {return getBeanListByNodeType(root, nodeType);}/*** 从root节点开始获取对应nodeType的node.* @param nodeType 节点类型* @return nodeType类型的节点集合*/public List<BeanTreeNode> getNodeListByNodeType(String nodeType) {return getNodeListByNodeType(root, nodeType);}/*** 从node节点开始获取对应nodeType的node.* @param node node节点* @param nodeType 节点类型* @return nodeType类型的节点集合*/public List<BeanTreeNode> getNodeListByNodeType(BeanTreeNode node, String nodeType) {List<BeanTreeNode> nodeList = new ArrayList<BeanTreeNode>();if(node==null){return nodeList;  }if (nodeType.equals(node.getNodeType())) {nodeList.add(node);} else {for (BeanTreeNode tempNode : node.getChildNodes()) {nodeList.addAll(getNodeListByNodeType(tempNode, nodeType));  }}return nodeList;}/*** 将树形结构转化为map.* @return*/public Map<String, List<Object>> toMap() {return toMap(root);}/*** 将对应节点及其子节点转化为map.* @param node 树节点* @return 转化后的map*/public Map<String, List<Object>> toMap(BeanTreeNode node) {Map<String, List<Object>> map = new HashMap<String, List<Object>>();toMap(node, map);return map;}/*** 根据传入的nodeType删除对应的节点以及其所有子节点.* @param nodeType*/public void delNodeByNodeType(String nodeType) {delNodeByNodeType(root, nodeType);}/*** 删除node节点下,类型为nodeType的节点和所有子节点* @param node* @param nodeType*/public void delNodeByNodeType(BeanTreeNode node, String nodeType) {List<BeanTreeNode> nodeList = getNodeListByNodeType(node, nodeType);for (BeanTreeNode beanTreeNode : nodeList) {beanTreeNode.getParentNode().getChildNodes().remove(beanTreeNode);}}/*** 从树结构里面删除bean和相关node.* @param bean bean*/public void delNodeByBean(Object bean) {BeanTreeNode node = getNodeByBean(bean);BeanTreeNode parentNode = node.getParentNode();List<BeanTreeNode> childNodes = parentNode.getChildNodes();Iterator<BeanTreeNode> it = childNodes.iterator();while (it.hasNext()) {BeanTreeNode beanTreeNode = it.next();if (node == beanTreeNode) {it.remove();}}}/*** 根据class返回对应的beanList.* @param cls class* @return beanList*/public <T> List<Object> getBeanListByClass(Class<T> cls) {return getBeanListByClass(root, cls);}/*** 根据class返回对应的beanList.* @param node 节点* @param cls class* @return beanList*/public <T> List<Object> getBeanListByClass(BeanTreeNode node, Class<T> cls) {List<Object> beanList = new ArrayList<Object>();Object bean = node.getBean();if (cls.isAssignableFrom(bean.getClass())) {beanList.add(bean);}List<BeanTreeNode> childNodes = node.getChildNodes();for (BeanTreeNode beanTreeNode : childNodes) {beanList.addAll(getBeanListByClass(beanTreeNode, cls));}return beanList;}/*** 将对应节点及其子节点转化为map.* @param node 树节点* @param map 用来保存结果的map*/private void toMap(BeanTreeNode node, Map<String, List<Object>> map) {String key = node.getNodeType();Object bean = node.getBean();if (map.containsKey(key)) {map.get(key).add(bean);} else {List<Object> list = new ArrayList<Object>();list.add(bean);map.put(key, list);}for (BeanTreeNode tempNode : node.getChildNodes()) {toMap(tempNode, map);}}
}
复制代码

测试树形工具

使用上面的xml进行测试,这里就不再做xml映射,假设存在上面xml所示的所有实体,“school-one”和“school-two”以及5个student,看看能否构造出想要的结构,测试类代码如下。

class SchoolInf {private String msg;public SchoolInf(String msg) {this.msg = msg;}
}class Student {private String name;public Student(String name) {this.name = name;}
}class School {private String name;public School(String name) {this.name = name;}
}public class Test {public static void main(String[] args) {SchoolInf schoolInf = new SchoolInf("2017-10-1XX省学校信息总汇");School school_one = new School("school-one");School school_two = new School("school-two");Student Jack = new Student("Jack");Student Rose = new Student("Rose");Student Jon = new Student("Jon");Student Bob = new Student("Bob");Student Alisa = new Student("Alisa");BeanTree tree = new BeanTree();BeanTreeNode root = tree.addNode(null, schoolInf, "root");BeanTreeNode school_node1 = tree.addNode(root, school_one, "school");BeanTreeNode school_node2 = tree.addNode(root, school_two, "school");tree.addNode(school_node1, Jack, "root");tree.addNode(school_node1, Rose, "root");tree.addNode(school_node1, Jon, "root");tree.addNode(school_node2, Bob, "root");tree.addNode(school_node2, Alisa, "root");System.out.println("end");}
}复制代码

我们通过调试观察树结构变量“tree”的值如下:

可以看出来能够构造出正确的结构,BeanTree中其他的一些方法这里就不在一一测试了。

更新记录

  • 2018/1/10,在BeanTree中添加更多的操作方法。

转载于:https://juejin.im/post/5add8ea8518825670e5cab3f

相关文章:

在Javascript中使用面向对象的编程

by Mike Koss March 26th, 2003 这是一篇&#xff0c;我个人认为最好的&#xff0c;Javascript面向对象编程的文章。翻译不好的地方&#xff0c;还望大家指正&#xff0c;谢谢。 如果您需要&#xff0c;可以访问下面的地址取得原文&#xff1a; http://mckoss.com/jscript/obj…

马斯克嘲笑「元宇宙」的想法,并给年轻人5条鸡汤

编译 | 禾木木出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;SpaceX 和特斯拉的CEO 马斯克在接受 The Babylon Bee 的采访中&#xff0c;当被问到元宇宙的问题时&#xff0c;马斯克只笑了笑。马斯克表示&#xff1a;“我对元宇宙这个概念没有什么印象&#xff0c;尽…

OpenLDAP自定义属性的启用

2019独角兽企业重金招聘Python工程师标准>>> # ucode# This multivalued field is used to record the values of the license or# registration plate associated with an individual.attributetype ( 2.16.840.1.113730.3.1.900 NAME ucode DESC user code …

硬中断与软中断的区别!

硬中断&#xff1a; 1. 硬中断是由硬件产生的&#xff0c;比如&#xff0c;像磁盘&#xff0c;网卡&#xff0c;键盘&#xff0c;时钟等。每个设备或设备集都有它自己的IRQ&#xff08;中断请求&#xff09;。基于IRQ&#xff0c;CPU可以将相应的请求分发到对应的硬件驱动上&am…

smarty模板

<?phprequire(../libs/Smarty.class.php);$smarty new Smarty;//$smarty->force_compile true;//$smarty->debugging true;//$smarty->caching true;//$smarty->cache_lifetime 120;$Name"Linux环境高级编程";$smarty->assign("name&qu…

乘“云原生”之风、踏“数字化”的浪,《新程序员003》开启预售!

12月30日&#xff0c;新年的钟声还有两天敲响&#xff0c;CSDN倾情策划出品的《新程序员003&#xff1a;云原生和全面数字化实践》&#xff08;以下简称《新程序员003》&#xff09;重磅开启预售&#xff01;新一年&#xff0c;新气象~预祝所有开发者在新的一年中大神附体&…

BZOJ4245 : [ONTAK2015]OR-XOR

按位考虑&#xff0c;逐步确定答案。 设当前是第i位&#xff0c;求出第i位的前缀异或和。 若存在m个0且所有数字异或和为0&#xff0c;那么答案的这一位可以为0&#xff0c;并把所有1的位置给标记为不可选。 否则答案的这一位只能是1。 时间复杂度$O(n\log n)$。 #include<c…

关键词排名下降怎么办-优八学院给你支招

优八学院下面为大家解决一下关于关键词排名下降的问题。在我们进行网站优化的时候&#xff0c;往往会出现关键词排名下降的现象。对于这种情况&#xff0c;我们要区别是否是正常的浮动&#xff0c;由于有时候搜索引擎也会发生错误&#xff0c;导致关键词排名下降&#xff0c;我…

全面解析 Kmeans 聚类算法(Python)

作者 | 泳鱼来源 | 算法进阶一、聚类简介Clustering (聚类)是常见的unsupervised learning (无监督学习)方法&#xff0c;简单地说就是把相似的数据样本分到一组&#xff08;簇&#xff09;&#xff0c;聚类的过程&#xff0c;我们并不清楚某一类是什么&#xff08;通常无标签信…

.htaccess的重写规则

.htaccess基本语法和应用 .htaccess是Apache服务器的一个非常强大的分布式配置文件。正确的理解和使用.htaccess文件&#xff0c;可以帮助我们优化自己的服务器或者虚拟主机。 如何启用htaccess 以windows为例&#xff0c;进入apache/conf目录&#xff0c;找到httpd.conf文件&a…

amaze ui各个模块简单说明

amaze ui各个模块简单说明 导航添加依据 http://amazeui.org/css/ 下面内容属学习笔记&#xff0c;如有理解偏差和错误请留言相告&#xff0c;感谢&#xff01;* &#xff08;官网这块写的很详细&#xff09; 一、基本样式 1.统一样式 说明了为什么使用Normalize&#xff0c;而…

由浅入深剖析.htaccess

1、.htaccess文件使用前提.htaccess的主要作用就是实现url改写&#xff0c;也就是当浏览器通过url访问到服务器某个文件夹时&#xff0c;作为主人&#xff0c;我们可以来接待这个url&#xff0c;具体地怎样接待它&#xff0c;就是此文件的作用。所有的访问都是通过URL实现&…

分享几个 Pyecharts 技巧,助你画出更直观/炫酷的图表

作者 | 俊欣来源 | 关于数据分析与可视化想必大家应该也已经看到很多关于数据分析的内容了&#xff0c;今天小编就为大家来分享一下国产可视化库pyecharts在绘制图表时一些的技巧&#xff0c;帮助读者画出更加酷炫以及可读性更高的图&#xff0c;当然在这之前呢&#xff0c;我们…

虚拟化--006 VCAC的sso配置成功

转载于:https://blog.51cto.com/williamliuwen/1686492

ionic app 开发和生产环境的配置

前言 像 Angular2 一样&#xff0c;希望 ionic 可以提供 2 个文件 ( environment.dev.ts 和 environment.prod.ts )&#xff0c;其中包含与开发和生产环境相对应的不同值的变量。在构建过程中&#xff0c;要在应用程序中绑定适当的文件。 实现步骤 在 src/config 中&#xff0c…

Android Properties 存储

1.初始化 1 private static void initProperties(){2 File logFile new File(Constants.PROGRESS_PROPERTIES);3 props new Properties();4 if(!logFile.exists()){5 //创建并初始化配置文件6 FileUtils.createFolder(Const…

php函数serialize()与unserialize()

php函数serialize()与unserialize()说明及案例。想要将已序列化的字符串变回 PHP 的值&#xff0c;可使用unserialize()。serialize()可处理除了resource之外的任何类型。甚至可以serialize()那些包含了指向其自身引用的数组。你正serialize()的数组&#xff0f;对象中的引用也…

2500 字全方面解读 Python 的格式化输出

作者 | 欣一来源 | Python爱好者集中营今天小编来和大家聊聊Python当中的格式化输出&#xff0c;希望会对大家所有帮助%占位符的使用我们先来看一下下面的这个例子&#xff0c;country_ "France" currency_ "Euro"print("%s is the currency of %s&…

python GUI编程( 二 ) (基于PyQt5)

第二节 本节介绍添加窗口图标&#xff0c;在窗口内添加按钮&#xff0c;在窗口内添加提示框。 导入模块&#xff1a; from PyQt5.QWidgets import QWidget,QPushButton,QApplication from PyQt5.QtGui import QIcon,QFont from PyQt5.QtCore import QCoreApplication import sy…

Linux+Apache2+openssl实现https验证

首先安装SSL&#xff0c;再编译安装APACHE&#xff0c;再配置证书即可 1.下载apache和openssl网址&#xff1a;http://www.apache.org http://www.openssl.org2.解压#tar zxvf httpd-2.0.54.tar.gz#tar zxvf openssl-0.9.7g.tar.gz3.编译安装openssl,这个软件主要是用来生…

践行科技向善,腾讯Light 把光引向厦门

作者 | 贾凯强出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;凛冬虽至&#xff0c;但沿着东南海域一路向南&#xff0c;总有寒风吹不灭的绿意&#xff0c;也有四季不败落的花香。今年的冬天厦门始终环绕着勃勃生机&#xff0c;也有无数的追光者来到了这里。因为关注…

【每天一点点】

>>>html 使用使用<a href"URL">ba bla bla</a>定义资源位置&#xff0c;使用<a href"#name"></a>跳转到name锚所在的位置&#xff1b;>>>eclipse的注释快捷键 方法一&#xff1a;使用Ctrl/快捷键&#xff0c;使…

模式的秘密-观察者模式(四)

区别对待观察者场景问题 两点需求&#xff1a; 第一&#xff1a;黄明女朋友只想接收下雨的天气预报。 第二&#xff1a;黄明老妈&#xff0c;想接收下雨或者下雪的天气预报。 解决思路&#xff1a; 情况之一&#xff1a; 如果天气晴天&#xff0c;按照黄明女朋友需要下雨添加&a…

PHP Webservice的发布与调用

PHP Webservice的发布与调用1. 环境配置 配置php.ini&#xff0c;把php_soap.dll前面的分号去掉&#xff0c;不然会报错 class soapserver not found重启apache后通过phpinfo()查看 这样是表示环境已经支持soap的webservice了&#xff0c;后面的事情就是写代码了。2. webserv…

全球首家!苹果市值达 3 万亿美元,创历史新高

作者 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;成立于 1976 年的苹果公司&#xff0c;耗时 44 年&#xff0c;终于在 2018 年首次达到 1 万亿美元的市值。自此之后&#xff0c;苹果的发展仿佛安装了“高速马达”&#xff0c;短短两年后的 2020 年 8 月…

Add Digits

题干就是给一个非负整数&#xff0c;把各位数加起来&#xff0c;若超过一位&#xff0c;则继续把各位加起来&#xff0c;直到和是一位数。 example&#xff1a; 39->12->3 坦白说我是看了第三个提示意识到的&#xff0c;所以说要找规律&#xff0c;先要暴力列举。 int ad…

JAVA多线程之Synchronized、wait、notify实例讲解

一、Synchronized synchronized中文解释是同步&#xff0c;那么什么是同步呢&#xff0c;解释就是程序中用于控制不同线程间操作发生相对顺序的机制&#xff0c;通俗来讲就是2点&#xff0c;第一要有多线程&#xff0c;第二当多个线程同时竞争某个资源的时候会有先后顺序。在ja…

匹夫细说C#:委托的简化语法,聊聊匿名方法和闭包

0x00 前言 通过上一篇博客《匹夫细说C#&#xff1a;庖丁解牛聊委托&#xff0c;那些编译器藏的和U3D给的》的内容&#xff0c;我们实现了使用委托来构建我们自己的消息系统的过程。但是在日常的开发中&#xff0c;仍然有很多开发者因为这样或那样的原因而选择疏远委托&#xff…

20个案例详解 Pandas 当中的数据统计分析与排序

作者 | 俊欣来源 | 关于数据分析与可视化今天小编来给大家讲一下Pandas模块当中的数据统计与排序&#xff0c;说到具体的就是value_counts()方法以及sort_values()方法。value_counts()方法&#xff0c;顾名思义&#xff0c;主要是用于计算各个类别出现的次数的&#xff0c;而s…

zend studio 8安装与汉化

http://archive.eclipse.org/technology/babel/update-site/R0.8.0/helios正确操作&#xff1a;1、大家可以用这个地址作为更新源&#xff08;操作&#xff1a;菜单栏中window->property->Installation/update->update 添加这个地址&#xff0c;并打勾&#xff09; 2、…