BrightLoong's Blog

使用树形结构保存实体

tree

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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的相关操作我并没有添加,如果需要可以自行添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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结构

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
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) {
//从当前父节点移除sNode
sNode.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,看看能否构造出想要的结构,测试类代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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”的值如下:

result

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

更新记录

  • 2018/1/10,在BeanTree中添加更多的操作方法。
坚持原创技术分享,您的支持将鼓励我继续创作!