我們通常會在應用中碰到樹形結構的內容,比如 文件夾/文件模型, 部門組織結構,目錄樹等等,通常在設計模式中叫做 compose 模式。
在數據庫中常常這樣表示: 我們以Catalog (分級目錄) 為例子
Catalog (分級目錄) |
字段名稱 | 字段 | 類型 | 備注 |
目錄ID | catalog_id | varchar(36) | pk, not null |
目錄名稱 | catalog_name | varchar(50) | not null |
父目錄ID | parent_id | varchar(36) | fk |
創建時間 | create_datetime | datetime | not null |
目錄描述 | description | varchar(200) | ? |
我們考慮在數據庫中一次將所有數據讀入內存,然后在內存中生成一個Tree,這樣可以減少數據庫的訪問,增加性能,并且只有的數據方式改變的時候,全部重新從數據庫中生成Tree,否則一直保持在內存中。
我們使用標準的DAO模式先生成 POJO類(Catalog)和DAO類(CatalogDAO)。
然后我們建立相對通用的 Tree 和 TreeNode 類。
Tree.java
package com.humpic.helper.tree;
import java.util.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public?abstract?class Tree {
??? protected?static Log log = LogFactory.getLog(Tree.class);
??? private Map treeNodeMaps =?new Hashtable();
??? private TreeNode root;
??? /**
???? * root if it's parent is empty
???? */
??? protected?void reload(List nodes) {
??????? log.info("tree will start reload all data");
???????
??????? synchronized (this) {
??????????? // initialize
??????????? treeNodeMaps.clear();
??????????? root =?null;
??????????? List treeNodes =?new Vector(nodes.size());
??????????? for (int i =?0; i < nodes.size(); i++) {
??????????????? TreeNode node =?this.transform(nodes.get(i)); // transform
??????????????? treeNodes.add(node);
??????????????? node.setTree(this);
??????????????? treeNodeMaps.put(node.getNodeId(), node);
??????????? }
???????????
??????????? for (int i =?0; i < treeNodes.size(); i++) {
??????????????? TreeNode node = (TreeNode) treeNodes.get(i);
??????????????? String parentId = node.getParentId();
??????????????? if (this.isRootNode(node)) {
??????????????????? if (root ==?null) {
??????????????????????? root = node;
??????????????????? } else {
??????????????????????? log.error("find more then one root node. ignore.");
??????????????????? }
??????????????? } else {
??????????????????? TreeNode parent = (TreeNode) treeNodeMaps.get(parentId);
??????????????????? if (parent !=?null) {
??????????????????????? parent.addChild(node);
??????????????????????? node.setParent(parent);
??????????????????? } else {
??????????????????????? log.warn("node [id="?+ node.getNodeId() +?"]: missing parent node.");
??????????????????? }
??????????????? }
??????????? }
??????? }
??????? if (root ==?null) {
??????????? log.error("the root node is not be defined");
??????? }
??? }
??? protected boolean isRootNode(TreeNode node) {
??????? return?StringUtils.isBlank(node.getParentId());
??? }
??? public TreeNode getRootNode() {
??????? return root;
??? }
??? public TreeNode getTreeNode(String nodeId) {
??????? return (TreeNode) treeNodeMaps.get(nodeId);
??? }
??? public?void addTreeNode(TreeNode node) {
??????? synchronized (this) {
??????????? treeNodeMaps.put(node.getNodeId(), node);
??????????? String parentId = node.getParentId();
??????????? if (StringUtils.isNotBlank(parentId)) {
??????????????? TreeNode parent = getTreeNode(parentId);
??????????????? if (parent !=?null) {
??????????????????? parent.addChild(node);
??????????????????? node.setParent(parent);
??????????????? } else {
??????????????????? log.error("parent cannot be found: "?+ node.getParentId());
??????????????? }
??????????? } else {
??????????????? if (root ==?null) {
??????????????????? root = node;
??????????????? } else {
??????????????????? log.error("find more then one root node. ignore.");
??????????????? }
??????????? }
??????? }
??? }
??? public?void deleteTreeNode(String nodeId) {
??????? synchronized (this) {
??????????? TreeNode node = getTreeNode(nodeId);
??????????? if (node ==?null)
??????????????? throw?new IllegalArgumentException(nodeId +?" cannot be found.");
??????????? if (node.getParent() ==?null) {
??????????????? root =?null;
??????????????? treeNodeMaps.clear();
??????????????? log.warn("the root node has been removed.");
??????????? } else {
??????????????? node.getParent().getChildren().remove(node);
??????????????? treeNodeMaps.remove(nodeId);
??????????????? List children = node.getAllChildren();
??????????????? for (int i =?0; i < children.size(); i++) {
??????????????????? TreeNode n = (TreeNode) children.get(i);
??????????????????? treeNodeMaps.remove(n.getNodeId());
??????????????? }
??????????? }
??????? }
??? }
??? /**
???? * <pre>
???? * Usage: Office ->
???? *
???? * public TreeNode transform(Object info) {
???? *???? OfficeInfo office_info = (OfficeInfo) info;
???? *???? TreeNode node = new TreeNode();
???? *???? node.setNodeId(office_info.getOfficeId());
???? *???? node.setParentId(office_info.getParentId());
???? *???? node.setBindData(office_info);
???? *???? return node;
???? * }
???? * </pre>
???? */
??? protected?abstract TreeNode transform(Object info);
}
TreeNode.java
package?com.humpic.helper.tree;
import java.util.List;
import java.util.Vector;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.ObjectUtils;
public?class TreeNode {
??? private Tree tree;
??? private TreeNode parent;
??? private List children =?new Vector();
??? private List childrenGroup =?new Vector();
??? private String nodeId;
??? private String parentId;
??? private Object bindData;
??? public String getNodeId() {
??????? return nodeId;
??? }
??? public?void setNodeId(String nodeId) {
??????? this.nodeId = nodeId;
??? }
??? public String getParentId() {
??????? return parentId;
??? }
??? public?void setParentId(String parentId) {
??????? this.parentId = parentId;
??? }
??? public Object getBindData() {
??????? return bindData;
??? }
??? public?void setBindData(Object bindData) {
??????? this.bindData = bindData;
??? }
??? public Tree getTree() {
??????? return tree;
??? }
??? public?void setTree(Tree tree) {
??????? this.tree = tree;
??? }
??? public?void setParent(TreeNode parent) {
??????? this.parent = parent;
??? }
??? public TreeNode getParent() {
??????? return?this.parent;
??? }
??? public List getChildren() {
??????? return?this.children;
??? }
??? public?void addChild(TreeNode node) {
??????? children.add(node);
??? }
??? /**
???? * get all children, and chilren's children
???? */
??? public List getAllChildren() {
??????? if (this.childrenGroup.isEmpty()) {
??????????? synchronized (this.tree) {
??????????????? for (int i =?0; i <?this.children.size(); i++) {
??????????????????? TreeNode node = (TreeNode) this.children.get(i);
??????????????????? this.childrenGroup.add(node);
??????????????????? this.childrenGroup.addAll(node.getAllChildren());
??????????????? }
??????????? }
??????? }
??????? return?this.childrenGroup;
??? }
??? /**
???? * get all children, and chilren's children
???? */
?? ?public?List getAllChildren(Predicate predicate) {
??????? List groups =?new Vector();
??????? fillAllChildren(groups, predicate);
??????? return groups;
??? }
??? private?void fillAllChildren(List groups, Predicate predicate) {
??????? for (int i =?0; i <?this.children.size(); i++) {
??????????? TreeNode node = (TreeNode) this.children.get(i);
??????????? if (predicate.evaluate(node)) {
??????????????? groups.add(node);
??????????????? node.fillAllChildren(groups, predicate);
??????????? }
??????? }
??? }
??? /**
???? * get all parents, and parent's parent
??? */
??? public List getParents() {
?? ??? ?List results = new Vector();
??? ??? TreeNode parent = this.getParent();
??? ??? while (parent != null) {
??? ??? ??? results.add(parent);
??? ??? ??? parent = parent.getParent();
??? ??? }
??? ??? return results;
??? }
??? /**
???? * A.isMyParent(B) == B is A' parent ? <br>
???? * root.isMyParent(null) == true; <br>
???? * root.isMyParent(*) == false <br>
???? * *.isMyParent(null) == false
???? */
??? public?boolean isMyParent(String nodeId) {
??????? TreeNode target = tree.getTreeNode(nodeId);
??????? TreeNode parent =?this.getParent();
??????? if (parent ==?null) {
??????????? return target ==?null;
??????? } else {
??????????? return parent.equals(target);
??????? }
??? }
??? /**
???? * A.isMyAncestor(B) == B is A' ancestor ? <br>
???? * *.isMyAncestor(null) == true;
???? */
??? public?boolean isMyAncestor(String nodeId) {
??????? TreeNode target = tree.getTreeNode(nodeId);
??????? if (target ==?null)
??????????? return?true;
??????? return target.getAllChildren().contains(this);
??? }
??? /**
???? * A.isMyBrother(B) == B is A' brother ? <br>
???? * *.isMyBrother(null) == false
???? */
??? public?boolean isMyBrother(String nodeId) {
??????? TreeNode target = tree.getTreeNode(nodeId);
??????? if (target ==?null)
??????????? return?false;
??????? TreeNode p1 =?this.getParent();
??????? TreeNode p2 = target.getParent();
??????? return ObjectUtils.equals(p1, p2);
??? }
}
然后建立業務 Tree
CatalogTree.java
package com.humpic.helper.tree;
import java.util.List;
import org.apache.commons.collections.Predicate;
public?class CatalogTree extends Tree {
??? private?static CatalogTree instance =?null;
??? private CatalogTree() {}
???
??? public?static synchronized CatalogTree getInstance() {
??????? if (instance ==?null) {
??????????? instance =?new CatalogTree();
??????????? instance.reloadCatalogs();
??????? }
??????? return instance;
??? }
??? protected TreeNode transform(Object info) {
??????? Catalog catalog = (Catalog) info;
??????? TreeNode node =?new TreeNode();
??????? node.setNodeId(catalog.getCatalogId());
??????? node.setParentId(catalog.getParentId());
??????? node.setBindData(catalog);
??????? return node;
??? }
??? public?void reloadCatalogs() {
??????? List nodes = CatalogDAO.getInstance().findAll();
??????? super.reload(nodes);
??? }
??? public Catalog getCatalogNode(String catalogId) {
??????? TreeNode node =?super.getTreeNode(catalogId);
??????? return node ==?null???null : (Catalog) node.getBindData();
??? }
}
最后,我們只要使用以下的語句就可以了:
1. CatalogTree.getInstance().getTreeNode(...)
2. CatalogTree.getInstance().getCatalogNode(...)
3. CatalogTree.getInstance().getRootNode()
然后通過 TreeNode,就可以得到 parent, parents 和 children, allChildren
文章來源:
http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!367.entry