在平时的开发工作中,经常遇到这样一个场景,在数据库中存储了具有父子关系的数据,需要将这些数据以树形结构的形式在界面上进行展示。本文的目的是提供了一个通用的编程模型,解决将具有父子关系的数据转换成树形结构的问题。如有不正之处,欢迎大家批评指正。
编程模型
我们以北京行政区划为例,讲解整个模型。
北京市:市辖区 县
市辖区:东城区 西城区 朝阳区 丰台区 石景山区 海淀区 门头沟区 房山区 通州区 顺义区 昌平区 大兴区 怀柔区 平谷区
县:密云县 延庆县
UML类图如下:
TreeNode:定义树的节点对象
- nodeId:树节点Id。
- nodeName:树节点名称。
- parentNodeId:树节点父Id。
- orderNum:节点在树中的排序号,在同一节点下进行排序。
- level:当前树节点所在的层级,根节点为第一层。
- parent:当前树节点的父节点。
- children:当前树节点的儿子节点。
- allChildren:当前树节点的子孙节点。
ITree:定义树对象要实现的方法。
- getTree():以 List 形式返回树的所有的 TreeNode 对象。
- getRoot():以 List 形式返回树的根节点,可能是一个或者多个。
- getTreeNode(String nodeId):根据 nodeId 返回对应的 TreeNode 对象。
Tree:实现 ITree 接口,提供树的完整功能。
- getTree():以 List 形式返回树的所有的 TreeNode 对象。
- getRoot():以 List 形式返回树的根节点,可能是一个或者多个。
- getTreeNode(String nodeId):根据 nodeId 返回对应的 TreeNode 对象。
ITreeNode:定义模板方法,构造树形结构的类要实现该接口,Tree 通过调用该接口中的方法获取 nodeId nodeName parentNodeId orderNum 数据。
- getNodeId():获取树节点Id。
- getNodeName():获取树节点名称。
- getParentNodeId():获取树节点父Id。
- getOrderNum():获取节点在树中的排序号,在同一节点下进行排序。
Org:定义行政区划类,实现 ItreeNode 接口。
实现代码
TreeNode类:
package com.ips.tree;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
/**
* <p>Title: 树节点</p>
* <p>Description:一棵树由许多包含父子关系的节点组成 </p>
* @author liuzhibo
* @date 2017年1月18日
*/
public class TreeNode {
//树节点ID
@JSONField(ordinal=1)
private String nodeId;
//树节点名称
@JSONField(ordinal=2)
private String nodeName;
//父节点ID
@JSONField(ordinal=3)
private String parentNodeId;
//节点在树中的排序号
@JSONField(ordinal=4)
private int orderNum;
//节点所在的层级
@JSONField(ordinal=5)
private int level;
private TreeNode parent;
//当前节点的二子节点
@JSONField(ordinal=6)
private List<TreeNode> children = new ArrayList<TreeNode>();
//当前节点的子孙节点
private List<TreeNode> allChildren = new ArrayList<TreeNode>();
public TreeNode(ITreeNode obj){
this.nodeId = obj.getNodeId();
this.nodeName = obj.getNodeName();
this.parentNodeId = obj.getNodeParentId();
this.orderNum = obj.getOrderNum();
}
public void addChild(TreeNode treeNode){
this.children.add(treeNode);
}
public void removeChild(TreeNode treeNode){
this.children.remove(treeNode);
}
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public String getParentNodeId() {
return parentNodeId;
}
public void setParentNodeId(String parentNodeId) {
this.parentNodeId = parentNodeId;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
public List<TreeNode> getChildren() {
return children;
}
public void setChildren(List<TreeNode> children) {
this.children = children;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
public List<TreeNode> getAllChildren() {
if(this.allChildren.isEmpty()){
for(TreeNode treeNode : this.children){
this.allChildren.add(treeNode);
this.allChildren.addAll(treeNode.getAllChildren());
}
}
return this.allChildren;
}
}
ITree接口:
package com.ips.tree;
import java.util.List;
public interface ITree {
public List<TreeNode> getTree();
public List<TreeNode> getRoot();
public TreeNode getTreeNode(String nodeId);
}
Tree类:
package com.ips.tree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class Tree implements ITree {
private HashMap<String, TreeNode> treeNodesMap = new HashMap<String, TreeNode>();
private List<TreeNode> treeNodesList = new ArrayList<TreeNode>();
public Tree(List<ITreeNode> list){
initTreeNodeMap(list);
initTreeNodeList();
}
private void initTreeNodeMap(List<ITreeNode> list){
TreeNode treeNode = null;
for(ITreeNode item : list){
treeNode = new TreeNode(item);
treeNodesMap.put(treeNode.getNodeId(), treeNode);
}
Iterator<TreeNode> iter = treeNodesMap.values().iterator();
TreeNode parentTreeNode = null;
while(iter.hasNext()){
treeNode = iter.next();
if(treeNode.getParentNodeId() == null || treeNode.getParentNodeId() == ""){
continue;
}
parentTreeNode = treeNodesMap.get(treeNode.getParentNodeId());
if(parentTreeNode != null){
treeNode.setParent(parentTreeNode);
parentTreeNode.addChild(treeNode);
}
}
}
private void initTreeNodeList(){
if(treeNodesList.size() > 0){
return;
}
if(treeNodesMap.size() == 0){
return;
}
Iterator<TreeNode> iter = treeNodesMap.values().iterator();
TreeNode treeNode = null;
while(iter.hasNext()){
treeNode = iter.next();
if(treeNode.getParent() == null){
this.treeNodesList.add(treeNode);
this.treeNodesList.addAll(treeNode.getAllChildren());
}
}
}
@Override
public List<TreeNode> getTree() {
return this.treeNodesList;
}
@Override
public List<TreeNode> getRoot() {
List<TreeNode> rootList = new ArrayList<TreeNode>();
if (this.treeNodesList.size() > 0) {
for (TreeNode node : treeNodesList) {
if (node.getParent() == null)
rootList.add(node);
}
}
return rootList;
}
@Override
public TreeNode getTreeNode(String nodeId) {
return this.treeNodesMap.get(nodeId);
}
}
ITreeNode 接口:
package com.ips.tree;
public interface ITreeNode {
public String getNodeId();
public String getNodeName();
public String getNodeParentId();
public Integer getOrderNum();
}
Org 类:
package com.ips.tree;
public class Org implements ITreeNode {
private String uuid;
private String parentId;
private String name;
private Integer orderNum;
private String code;
private String type;
public Org(){
}
public Org(String uuid, String parentId, String name, Integer orderNum, String code, String type){
this.uuid = uuid;
this.parentId = parentId;
this.name = name;
this.orderNum = orderNum;
this.code = code;
this.type = type;
}
@Override
public String getNodeId() {
return this.uuid;
}
@Override
public String getNodeName() {
return this.name;
}
@Override
public String getNodeParentId() {
return this.parentId;
}
@Override
public Integer getOrderNum() {
return this.orderNum;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void setOrderNum(Integer orderNum) {
this.orderNum = orderNum;
}
}
TreeDemo 类:执行该类的 main 方法,查看效果。
package com.ips.tree;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
public class TreeDemo {
public static void main(String[] args) {
Tree tree = new Tree(genOrgList());
TreeNode treeNode = tree.getTreeNode("2");
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(); // 构造方法里,也可以直接传需要序列化的属性名字
filter.getExcludes().add("parent");
filter.getExcludes().add("allChildren");
String data = JSONObject.toJSONString(treeNode, filter);
System.out.println(data);
}
public static List<ITreeNode> genOrgList(){
List<ITreeNode> list = new ArrayList<ITreeNode>();
Org org = new Org("2", "1", "北京市", 2, "110000", "2");
list.add(org);
org = new Org("3", "2", "市辖区", 3, "110100", "3");
list.add(org);
org = new Org("4", "3", "东城区", 4, "110101", "4");
list.add(org);
org = new Org("5", "3", "东城区", 5, "110102", "4");
list.add(org);
org = new Org("6", "3", "东城区", 6, "110105", "4");
list.add(org);
org = new Org("7", "3", "东城区", 7, "110106", "4");
list.add(org);
org = new Org("8", "3", "东城区", 8, "110107", "4");
list.add(org);
org = new Org("9", "3", "东城区", 9, "110108", "4");
list.add(org);
org = new Org("10", "3", "东城区", 10, "110109", "4");
list.add(org);
org = new Org("11", "3", "东城区", 11, "110111", "4");
list.add(org);
org = new Org("12", "3", "东城区", 12, "110112", "4");
list.add(org);
org = new Org("13", "3", "东城区", 13, "110113", "4");
list.add(org);
org = new Org("14", "3", "东城区", 14, "110114", "4");
list.add(org);
org = new Org("15", "3", "东城区", 15, "110115", "4");
list.add(org);
org = new Org("16", "3", "东城区", 16, "110116", "4");
list.add(org);
org = new Org("17", "3", "东城区", 17, "110117", "4");
list.add(org);
org = new Org("18", "2", "县", 3, "110200", "3");
list.add(org);
org = new Org("19", "18", "密云县", 19, "110228", "4");
list.add(org);
org = new Org("20", "18", "延庆县", 20, "110229", "4");
list.add(org);
return list;
}
}
执行结果如下:
{
"nodeId": "2",
"nodeName": "北京市",
"parentNodeId": "1",
"orderNum": 2,
"level": 0,
"children": [{
"nodeId": "18",
"nodeName": "县",
"parentNodeId": "2",
"orderNum": 3,
"level": 0,
"children": [{
"nodeId": "19",
"nodeName": "密云县",
"parentNodeId": "18",
"orderNum": 19,
"level": 0,
"children": []
},
{
"nodeId": "20",
"nodeName": "延庆县",
"parentNodeId": "18",
"orderNum": 20,
"level": 0,
"children": []
}]
},
{
"nodeId": "3",
"nodeName": "市辖区",
"parentNodeId": "2",
"orderNum": 3,
"level": 0,
"children": [{
"nodeId": "17",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 17,
"level": 0,
"children": []
},
{
"nodeId": "15",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 15,
"level": 0,
"children": []
},
{
"nodeId": "16",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 16,
"level": 0,
"children": []
},
{
"nodeId": "13",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 13,
"level": 0,
"children": []
},
{
"nodeId": "14",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 14,
"level": 0,
"children": []
},
{
"nodeId": "11",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 11,
"level": 0,
"children": []
},
{
"nodeId": "12",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 12,
"level": 0,
"children": []
},
{
"nodeId": "10",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 10,
"level": 0,
"children": []
},
{
"nodeId": "7",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 7,
"level": 0,
"children": []
},
{
"nodeId": "6",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 6,
"level": 0,
"children": []
},
{
"nodeId": "5",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 5,
"level": 0,
"children": []
},
{
"nodeId": "4",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 4,
"level": 0,
"children": []
},
{
"nodeId": "9",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 9,
"level": 0,
"children": []
},
{
"nodeId": "8",
"nodeName": "东城区",
"parentNodeId": "3",
"orderNum": 8,
"level": 0,
"children": []
}]
}]
}
注:该示例中使用了 alibaba 的 fastjson 实现类对象序列化,maven 依赖如下:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.20</version>
</dependency>