
閱讀原文請訪問我的博客BrightLoong's Blog
之前在項目需要實現(xiàn)一個功能——將xml文件映射成實體,然后對映射的實體進行邏輯處理,最后保存到數(shù)據(jù)庫中;由于xml結(jié)構(gòu)的數(shù)據(jù)是結(jié)構(gòu)化的數(shù)據(jù),所以需要保證保存的數(shù)據(jù)具有正確的主外鍵關(guān)聯(lián)。如下所示,是一個需要保存到數(shù)據(jù)庫的xml文件。當(dāng)映射成對應(yīng)的實體school和student的時候,我們需要知道“school-one”下面有哪些學(xué)生,“school-two”下面有哪些學(xué)生,這個時候想到了使用樹形結(jié)構(gòu)來保存實體,讓實體之間依然存在關(guān)聯(lián)關(guān)系。
<school-inf>
<msg>2017-10-1XX省學(xué)校信息總匯</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>
樹形工具
以下是樹形工具類的實現(xiàn),包含了樹形節(jié)點類和樹形結(jié)構(gòu)類,由于代碼中注釋已經(jīng)比較全面,所以不做過多的說明。
樹形節(jié)點類BeanTreeNode.java
每一個節(jié)點對應(yīng)一個實體,節(jié)點包含了實體信息,為了保證實體之間的關(guān)聯(lián)關(guān)系,需要留有父節(jié)點信息,所有的子節(jié)點信息。由此推斷出,節(jié)點的主要成員有
- 父節(jié)點信息
- 所有子節(jié)點信息
- 當(dāng)前實體信息
為了方便操作,我還多增加了id和pid(parent id),以及節(jié)點類型(nodeType)。對id的相關(guān)操作我并沒有添加,如果需要可以自行添加。
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 實體樹形結(jié)構(gòu)點
* BeanTreeNode
* @author BrightLoong
* @version 1.0
*
*/
public class BeanTreeNode {
/**標識id*/
private String id;
/**父id標識,為了方便獲取冗余出來*/
private String pid;
/**父節(jié)點*/
private BeanTreeNode parentNode;
/**節(jié)點類型*/
private String nodeType;
/**節(jié)點值*/
private Object bean;
/**子節(jié)點*/
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;
}
/**
* 是否具有子節(jié)點
* @return true or false
*/
public boolean haveChild() {
return !CollectionUtils.isEmpty(childNodes);
}
}
樹形結(jié)構(gòu)類BeanTree.java
BeanTree.java里面包含了如下的一些常用操作:
- 返回根節(jié)點
- 返回最后添加節(jié)點
- 判斷是否具有子節(jié)點
- 添加節(jié)點
- 移動節(jié)點到其他節(jié)點下
- 獲取對應(yīng)nodeType的所有節(jié)點或?qū)嶓w
- 根據(jù)實體獲取節(jié)點
- 獲取父節(jié)點
- 轉(zhuǎn)化為map結(jié)構(gòu)
代碼如下
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;
/**
* 實體樹形結(jié)構(gòu)
* BeanTree
* @author BrightLoong
* @version 1.0
*
*/
public class BeanTree {
/**根節(jié)點*/
private BeanTreeNode root;
/**
* 最新添加的節(jié)點
*/
private BeanTreeNode currentNode;
/**
* @return the currentNode
*/
public BeanTreeNode getCurrentNode() {
return currentNode;
}
/**
* @return the root
*/
public BeanTreeNode getRoot() {
return root;
}
/**
* 判斷節(jié)點是否有子節(jié)點.
* @param node 要判斷的節(jié)點
* @return true or false
*/
public boolean haveChild(BeanTreeNode node) {
return CollectionUtils.isEmpty(node.getChildNodes());
}
/**
* 在父節(jié)點上面添加節(jié)點,并返回天添加的節(jié)點.
* @param parentNode 父節(jié)點
* @param bean 要添加的bean
* @param nodeType 節(jié)點類型
* @return 返回包含bean的節(jié)點
*/
public BeanTreeNode addNode(BeanTreeNode parentNode, Object bean, String nodeType) {
BeanTreeNode node;
if (bean == null) {
return null;
}
//如果沒有父節(jié)點說明為root根節(jié)點
if (parentNode == null) {
node = root = new BeanTreeNode(null, nodeType, bean);
} else {
//創(chuàng)建子節(jié)點,并添加到父節(jié)點上
node = new BeanTreeNode(parentNode, nodeType, bean);
parentNode.getChildNodes().add(node);
}
currentNode = node;
return node;
}
/**
* 將當(dāng)期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);
}
/**
* 將當(dāng)期node-sNode,以及sNode下的子Node,掛到dNode下
* @param sNode 源node
* @param dNode 目的父node
*/
public void removeTo(BeanTreeNode sNode, BeanTreeNode dNode) {
//從當(dāng)前父節(jié)點移除sNode
sNode.getParentNode().getChildNodes().remove(sNode);
//將sNode移到dNode下
dNode.getChildNodes().add(sNode);
//修改sNode的父Id和父節(jié)點
sNode.setPid(dNode.getId());
sNode.setParentNode(dNode);
}
/**
* 獲取父bean.
* @param bean 子bean
* @return 返回父bean
*/
public Object getParentBean(Object bean) {
return getNodeByBean(bean).getParentNode().getBean();
}
/**
* 根據(jù)傳入的bean獲取bean下面對應(yīng)類型的子bean.
* @param bean 當(dāng)前bean
* @param nodeType 節(jié)點類型
* @return 子bean的集合
*/
public List<Object> getBeanListByBeanAndNodeType(Object bean, String nodeType) {
BeanTreeNode node = getNodeByBean(bean);
return getBeanListByNodeType(node, nodeType);
}
/**
* 根據(jù)傳入的bean獲取包含bean的Node節(jié)點
* @param node 當(dāng)前node
* @param bean 要查找的bean
* @return node節(jié)點
*/
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;
}
/**
* 根據(jù)傳入的bean獲取root節(jié)點下包含bean的Node節(jié)點
* @param bean 要查找的bean
* @return node節(jié)點
*/
public BeanTreeNode getNodeByBean(Object bean) {
return getNodeByBean(root, bean);
}
/**
* 根據(jù)節(jié)點類型返回當(dāng)前節(jié)點下對應(yīng)節(jié)點類型的bean的list集合.
* 默認如果當(dāng)前節(jié)點滿足類型,那么當(dāng)前節(jié)點不會存在相同類型的子節(jié)點
* @param node 當(dāng)前節(jié)點
* @param nodeType 節(jié)點類型
* @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;
}
/**
* 根據(jù)節(jié)點類型返回根節(jié)點下對應(yīng)節(jié)點類型的bean的list集合.
* @param nodeType 節(jié)點類型
* @return
*/
public <T> List<T> getBeanListByNodeType(String nodeType) {
return getBeanListByNodeType(root, nodeType);
}
/**
* 從root節(jié)點開始獲取對應(yīng)nodeType的node.
* @param nodeType 節(jié)點類型
* @return nodeType類型的節(jié)點集合
*/
public List<BeanTreeNode> getNodeListByNodeType(String nodeType) {
return getNodeListByNodeType(root, nodeType);
}
/**
* 從node節(jié)點開始獲取對應(yīng)nodeType的node.
* @param node node節(jié)點
* @param nodeType 節(jié)點類型
* @return nodeType類型的節(jié)點集合
*/
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;
}
/**
* 將樹形結(jié)構(gòu)轉(zhuǎn)化為map.
* @return
*/
public Map<String, List<Object>> toMap() {
return toMap(root);
}
/**
* 將對應(yīng)節(jié)點及其子節(jié)點轉(zhuǎn)化為map.
* @param node 樹節(jié)點
* @return 轉(zhuǎn)化后的map
*/
public Map<String, List<Object>> toMap(BeanTreeNode node) {
Map<String, List<Object>> map = new HashMap<String, List<Object>>();
toMap(node, map);
return map;
}
/**
* 根據(jù)傳入的nodeType刪除對應(yīng)的節(jié)點以及其所有子節(jié)點.
* @param nodeType
*/
public void delNodeByNodeType(String nodeType) {
delNodeByNodeType(root, nodeType);
}
/**
* 刪除node節(jié)點下,類型為nodeType的節(jié)點和所有子節(jié)點
* @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);
}
}
/**
* 從樹結(jié)構(gòu)里面刪除bean和相關(guān)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();
}
}
}
/**
* 根據(jù)class返回對應(yīng)的beanList.
* @param cls class
* @return beanList
*/
public <T> List<Object> getBeanListByClass(Class<T> cls) {
return getBeanListByClass(root, cls);
}
/**
* 根據(jù)class返回對應(yīng)的beanList.
* @param node 節(jié)點
* @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;
}
/**
* 將對應(yīng)節(jié)點及其子節(jié)點轉(zhuǎn)化為map.
* @param node 樹節(jié)點
* @param map 用來保存結(jié)果的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映射,假設(shè)存在上面xml所示的所有實體,“school-one”和“school-two”以及5個student,看看能否構(gòu)造出想要的結(jié)構(gòu),測試類代碼如下。
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省學(xué)校信息總匯");
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");
}
}
我們通過調(diào)試觀察樹結(jié)構(gòu)變量“tree”的值如下:

可以看出來能夠構(gòu)造出正確的結(jié)構(gòu),BeanTree中其他的一些方法這里就不在一一測試了。
更新記錄
- 2018/1/10,在BeanTree中添加更多的操作方法。