java通用的构造树的工具类

一些树形结构的数据,经常是以 id、parent_id、其他属性这种方式存放在数据库里的,直接查询出来的就是这些带有id、parent_id值的javabean的list,并不是树形的结构,这次项目需要,我直接把对应这种情况的写成一个通用的类。大概的思路就是用map把id还有子节点映射起来,这样就可以做到从树的某一节点往下遍历。另外用一个map把id还有节点本身映射起来,因为每个节点知道它的parent_id,通过这个map就能得到父节点,这样就可以往上遍历了。

直接贴代码:

1.先是一个接口定义了一些访问树的方法,大部分都需要把map作为参数传递进去,然后再定义两个从list得到map的方法。

BaseTree.java

package com.csair.csm.util;

import java.util.List;
import java.util.Map;

/**
 * 基础树服务接口
 * <br>!注意:
 * <br>不建议使用dubbo远程调用该接口的方法(涉及大量数据传输(所有节点的List、Map);find方法涉及回调,需使用配置文件注册服务)
 * @author abo
 * @param <N>树节点
 * @param <K>树节点id(唯一标识)
 */
public interface BaseTree<N, K> {
	
	/**
	 * 获取所有节点key - 子节点List 映射Map
	 * @param nodeList	所有节点List
	 * @return
	 */
	Map<K, List<N>> getAllNodeAsMap(List<N> nodeList);
	
	/**
	 * 获取所有节点的key - 自己的映射Map
	 * @param nodeList	所有节点List
	 * @return
	 */
	Map<K, N> getAllNodeSelfMap(List<N> nodeList);
	
	/**
	 * 从某节点开始往上级搜索
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @param selfMap		所有节点的key - 自己的映射(可通过getAllNodeSelfMap获取)
	 * @return		匹配条件的节点集合(按层级从大到小排列)
	 */
	List<N> findUp(N node, BaseTreeSearcher<N> searcher, boolean needSelf, Map<K, N> selfMap);
	
	/**
	 * 从某节点开始往上级搜索,找到一个符合条件的就返回
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @param selfMap		所有节点的key - 自己的映射(可通过getAllNodeSelfMap获取)
	 * @return		匹配条件的第一个节点(层级大的优先)	找不到则返回null
	 */
	N findUpFirst(N node, BaseTreeSearcher<N> searcher, boolean needSelf, Map<K, N> selfMap);
	
	/**
	 * 从某节点开始往下级搜索
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @param childMap		所有节点key - 子节点List 映射(可通过getAllNodeAsMap获取)
	 * @return		匹配条件的节点集合(按层级从小到大排列)
	 */
	List<N> findDown(N node, BaseTreeSearcher<N> searcher, boolean needSelf, Map<K, List<N>> childMap);
	
	/**
	 * 从某节点开始往下级搜索,找到一个符合条件的就返回
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @param childMap		所有节点key - 子节点List 映射(可通过getAllNodeAsMap获取)
	 * @return		匹配条件的第一个节点(层级小的优先)	找不到则返回null
	 */
	N findDownFirst(N node, BaseTreeSearcher<N> searcher, boolean needSelf, Map<K, List<N>> childMap);
	
	/**
	 * 获得某节点的所有祖先节点
	 * @param node		当前节点
	 * @param selfMap		所有节点的key - 自己的映射(可通过getAllNodeSelfMap获取)
	 * @param needSelf		是否包含自己
	 * @return		祖先节点集合(按层级从大到小排列)
	 */
	List<N> getParentsNode(N node, Map<K, N> selfMap, boolean needSelf);

	/**
	 * 获取某节点的所有后代节点
	 * @param node	当前节点
	 * @param childMap	所有节点key - 子节点List 映射(可通过getAllNodeAsMap获取)
	 * @param needSelf	是否包含自己
	 * @return		后代节点集合(按层级从小到大排列)
	 */
	List<N> getSubNode(N node, Map<K, List<N>> childMap, boolean needSelf);
	
	/**
	 * 获取某节点下面的所有叶子
	 * @param node	当前节点
	 * @param childMap	所有节点key - 子节点List 映射(可通过getAllNodeAsMap获取)
	 * @return		叶子节点集合(如果自己是叶子,则集合中为自己)
	 */
	List<N> getSubLeaf(N node, Map<K, List<N>> childMap);
	
	/**
	 * 获取某节点的父节点(直接上级)
	 * @param node
	 * @param selfMap	所有节点的key - 自己的映射(可通过getAllNodeSelfMap获取)
	 * @return		没有则返回null
	 */
	N getParent(N node, Map<K, N> selfMap);
	
	/**
	 * 获取某节点的所有孩子(直接下级)
	 * @param node
	 * @param childMap	所有节点key - 子节点List 映射(可通过getAllNodeAsMap获取)
	 * @return		没有则返回null
	 */
	List<N> getChildren(N node, Map<K, List<N>> childMap);
	
	/**
	 * 判断一个节点是不是叶子
	 * @param node
	 * @param childMap	所有节点key - 子节点List 映射(可通过getAllNodeAsMap获取)
	 * @return
	 */
	boolean isLeaf(N node, Map<K, List<N>> childMap);
}

2.再定义一个抽象类实现这个接口的所有方法,保留两个抽象方法,用来具体实现获取每个节点的id、parent_id(因为并不知道具体类型id、parent_id的属性名是什么)。

BaseTreeImpl.java

package com.csair.csm.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基础树服务实现类
 * @author abo
 * @param <N>树节点
 * @param <K>树节点id(唯一标识)(K须具有有效的hashCode与equals方法)
 */
public abstract class BaseTreeImpl<N, K> implements BaseTree<N, K> {
	
	/**
	 * 获取节点唯一标识方法
	 * @param node
	 * @return
	 */
	protected abstract K getKey(N node);
	
	/**
	 * 获取节点父节点唯一标识方法
	 * @param node
	 * @return
	 */
	protected abstract K getParentKey(N node);
	
	@Override
	public Map<K, List<N>> getAllNodeAsMap(List<N> nodeList) {
		//map初始化容量(假设为满二叉树)
		int initialCapacity = Math.max((int) ((nodeList.size() - 1)/2.0f/.75f) + 1, 16);
		//建一个map来存放 父节点no到子节点列表 的映射
		Map<K, List<N>> childMap = new HashMap<>(initialCapacity);
		//遍历一遍
		for (N node : nodeList) {
			//取出它父节点的子节点列表
			List<N> childList = childMap.get(getParentKey(node));
			//如果还没有子节点列表,就新建一个
			if (childList == null) {
				childList = new ArrayList<>();
				childMap.put(getParentKey(node), childList);
			}
			//把自己放进去
			childList.add(node);
		}
		return childMap;
	}

	@Override
	public Map<K, N> getAllNodeSelfMap(List<N> nodeList) {
		//map初始化容量
		int initialCapacity = Math.max((int) (nodeList.size()/.75f) + 1, 16);
		//新建一个map
		Map<K, N> selfMap = new HashMap<>(initialCapacity);
		//遍历所有
		for (N node : nodeList) {
			//放进去map
			selfMap.put(getKey(node), node);
		}
		return selfMap;
	}
	
	@Override
	public List<N> findUp(N node, BaseTreeSearcher<N> searcher,
			boolean needSelf, Map<K, N> selfMap) {
		//新建一个结果集
		List<N> results = new ArrayList<>();
		//这个变量去存储每个节点
		N currentNode;
		//先得到第一个
		currentNode = node;
		//需不需要搜索第一个
		if (needSelf && searcher.search(currentNode)) {
			results.add(currentNode);
		}
		//逐级往上搜索
		do {
			//取到父节点
			currentNode = getParent(currentNode, selfMap);
			if (currentNode == null) {
				//结束了
				break;
			}
			//搜索
			if (searcher.search(currentNode)) {
				results.add(currentNode);
			}
		} while (true);
		return results;
	}
	
	@Override
	public N findUpFirst(N node, BaseTreeSearcher<N> searcher,
			boolean needSelf, Map<K, N> selfMap) {
		// 这个变量去存储每个节点
		N currentNode;
		// 先得到第一个
		currentNode = node;
		// 需不需要搜索第一个
		if (needSelf && searcher.search(currentNode)) {
			//返回
			return currentNode;
		}
		// 逐级往上搜索
		do {
			// 取到父节点
			currentNode = getParent(currentNode, selfMap);
			if (currentNode == null) {
				// 结束了
				break;
			}
			// 搜索
			if (searcher.search(currentNode)) {
				//返回
				return currentNode;
			}
		} while (true);
		//这里一定是null了
		return currentNode;
	}

	@Override
	public List<N> findDown(N node, BaseTreeSearcher<N> searcher,
			boolean needSelf, Map<K, List<N>> childMap) {
		//先获得所有后代
		List<N> childList = getSubNode(node, childMap, needSelf);
		//新建一个结果集
		List<N> resultsList = new ArrayList<>();
		//逐个去搜索
		for (N childNode : childList) {
			if (searcher.search(childNode)) {
				resultsList.add(childNode);
			}
		}
		return resultsList;
	}
	
	@Override
	public N findDownFirst(N node, BaseTreeSearcher<N> searcher,
			boolean needSelf, Map<K, List<N>> childMap) {
		// 如果需要找自己
		if (needSelf && searcher.search(node)) {
			// 返回
			return node;
		}
		// 建一个集合用来做广度遍历
		List<N> nodeList = new ArrayList<>();
		// 先把自己放进去
		nodeList.add(node);
		// 进行广度遍历
		int i = nodeList.size() - 1; // 遍历下标
		List<N> childList; // 存放直接下级
		while (true) {
			// 获取孩子
			childList = getChildren(node, childMap);
			if (childList != null) {
				// 存在就加进去
				nodeList.addAll(childList);
			}
			i++;
			if (i >= nodeList.size()) {
				// 如果越界了,就跳出
				break;
			} else {
				// 否则node指向下一个
				node = nodeList.get(i);
				// 找下一个
				if (searcher.search(node)) {
					// 匹配就直接返回
					return node;
				}
			}
		}
		// 到这里说明没找到,返回null
		return null;
	}

	@Override
	public List<N> getParentsNode(N node, Map<K, N> selfMap, boolean needSelf) {
		//无条件往上搜索
		return findUp(node, new BaseTreeSearcher<N>() {
			@Override
			public boolean search(N node) {
				//直接返回true
				return true;
			}
		}, needSelf, selfMap);
	}

	@Override
	public List<N> getSubNode(N node, Map<K, List<N>> childMap, boolean needSelf) {
		//建一个空的结果
		List<N> nodeList = new ArrayList<>();
		//如果需要自己
		if (needSelf) {
			//先把自己放进去
			nodeList.add(node);
		}
		//进行广度遍历找到所有后代
		int i = nodeList.size() - 1;		//遍历下标
		List<N> childList;					//存放直接下级
		while(true){
			//获取孩子
			childList = getChildren(node, childMap);
			if (childList != null) {
				//存在就加进去
				nodeList.addAll(childList);
			}
			i++;
			if (i >= nodeList.size()) {
				//如果越界了,就跳出
				break;
			} else {
				//否则node指向下一个
				node = nodeList.get(i);
			}
		}
		return nodeList;
	}
	
	@Override
	public List<N> getSubLeaf(N node, final Map<K, List<N>> childMap) {
		// 搜索它的后代
		return findDown(node, new BaseTreeSearcher<N>() {
			@Override
			public boolean search(N node) {
				// 是不是叶子
				return isLeaf(node, childMap);
			}
		}, true, childMap);
	}
	
	@Override
	public N getParent(N node, Map<K, N> selfMap) {
		return selfMap.get(getParentKey(node));
	}

	@Override
	public List<N> getChildren(N node, Map<K, List<N>> childMap) {
		return childMap.get(getKey(node));
	}

	@Override
	public boolean isLeaf(N node, Map<K, List<N>> childMap) {
		if (getChildren(node, childMap) == null) {
			// 没有下级,说明是叶子
			return true;
		}
		return false;
	}
	
}

3.这个接口只是用来在对树进行搜索时提供回调,实现具体的搜索逻辑。

BaseTreeSearcher.java

package com.csair.csm.util;

/**
 * 自定义树搜索器
 * @author abo
 * @param <N> 树节点
 */
public interface BaseTreeSearcher<N> {
	
	/**
	 * 当该方法返回true时,表示匹配到该节点
	 * @param node	当前树节点
	 * @return
	 */
	boolean search(N node);
}

这样基本就搞定了,在具体使用的时候,一种方法是接口和类分别继承1和2,直接提供这些树形操作的方法。

还有一种像这样:

private static final BaseTree<OrgTreeNode, String> baseTree = new BaseTreeImpl<OrgTreeNode, String>() {
    		@Override
		protected String getKey(OrgTreeNode node) {
			return node.getOrgNo();
		}
		@Override
		protected String getParentKey(OrgTreeNode node) {
			return node.getParentOrgNo();
		}
};

直接new一个静态常量,然后就可以像一个工具一样调用它的方法了。

4.后来考虑到每次调用都要把map作为参数传递也不太方便,又增加了一个类,就是用树的所有节点构造一棵树,然后就可以直接在树上进行操作了。

Tree.java

package com.csair.csm.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 一棵树
 * @author abo
 * @param <N>树节点
 * @param <K>树节点id(唯一标识)(K须具有有效的hashCode与equals方法)
 */
public abstract class Tree<N, K> {
	
	/**
	 * 树上所有节点的列表
	 */
	private List<N> allNodes;
	
	/**
	 * 树根
	 */
	private N root;
	
	/**
	 * 节点到其子节点的映射
	 */
	private Map<K, List<N>> childMap;
	
	/**
	 * 节点到自身的映射
	 */
	private Map<K, N> selfMap;
	
	public List<N> getAllNodes() {
		return allNodes;
	}

	public void setAllNodes(List<N> allNodes) {
		this.allNodes = allNodes;
	}

	public N getRoot() {
		if (root == null) {
			// 随便找一个的最上级的祖先,就是根节点
			List<N> tempList = baseTree.getParentsNode(this.getAllNodes().get(0), this.getSelfMap(), true);
			root = tempList.get(tempList.size() - 1);
		}
		return root;
	}

	public void setRoot(N root) {
		this.root = root;
	}

	public Map<K, List<N>> getChildMap() {
		if (childMap == null) {
			// 初始化
			childMap = baseTree.getAllNodeAsMap(this.getAllNodes());
		}
		return childMap;
	}

	public void setChildMap(Map<K, List<N>> childMap) {
		this.childMap = childMap;
	}

	public Map<K, N> getSelfMap() {
		if (selfMap == null) {
			selfMap = baseTree.getAllNodeSelfMap(this.getAllNodes());
		}
		return selfMap;
	}

	public void setSelfMap(Map<K, N> selfMap) {
		this.selfMap = selfMap;
	}
	
	/**
	 * 获取节点唯一标识方法
	 * @param node
	 * @return
	 */
	protected abstract K getKey(N node);
	
	/**
	 * 获取节点父节点唯一标识方法
	 * @param node
	 * @return
	 */
	protected abstract K getParentKey(N node);
	
	/**
	 * 树形操作的工具
	 */
	private final BaseTree<N, K> baseTree = new BaseTreeImpl<N, K>() {
		@Override
		protected K getKey(N node) {
			return Tree.this.getKey(node);
		}
		@Override
		protected K getParentKey(N node) {
			return Tree.this.getParentKey(node);
		}
	};
	
	/**
	 * 构造方法,传入所有树节点来构造一棵树
	 * @param allNodes
	 */
	public Tree(Collection<N> allNodes) {
		this.setAllNodes(new ArrayList<N>(allNodes));
	}
	
	/**
	 * 从某节点开始往上级搜索
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @return		匹配条件的节点集合(按层级从大到小排列)
	 */
	public List<N> findUp(N node, BaseTreeSearcher<N> searcher, boolean needSelf) {
		return baseTree.findUp(node, searcher, needSelf, this.getSelfMap());
	}
	
	/**
	 * 从某节点开始往上级搜索,找到一个符合条件的就返回
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @return		匹配条件的第一个节点(层级大的优先)	找不到则返回null
	 */
	public N findUpFirst(N node, BaseTreeSearcher<N> searcher, boolean needSelf) {
		return baseTree.findUpFirst(node, searcher, needSelf, this.getSelfMap());
	}
	
	/**
	 * 从某节点开始往下级搜索
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @return		匹配条件的节点集合(按层级从小到大排列)
	 */
	public List<N> findDown(N node, BaseTreeSearcher<N> searcher, boolean needSelf) {
		return baseTree.findDown(node, searcher, needSelf, this.getChildMap());
	}
	
	/**
	 * 从某节点开始往下级搜索,找到一个符合条件的就返回
	 * @param node		当前节点
	 * @param searcher		自定义搜索条件
	 * @param needSelf		是否搜索自己
	 * @return		匹配条件的第一个节点(层级小的优先)	找不到则返回null
	 */
	public N findDownFirst(N node, BaseTreeSearcher<N> searcher, boolean needSelf) {
		return baseTree.findDownFirst(node, searcher, needSelf, this.getChildMap());
	}
	
	/**
	 * 获得某节点的所有祖先节点
	 * @param node		当前节点
	 * @param needSelf		是否包含自己
	 * @return		祖先节点集合(按层级从大到小排列)
	 */
	public List<N> getParentsNode(N node, boolean needSelf) {
		return baseTree.getParentsNode(node, this.getSelfMap(), needSelf);
	}

	/**
	 * 获取某节点的所有后代节点
	 * @param node	当前节点
	 * @param needSelf	是否包含自己
	 * @return		后代节点集合(按层级从小到大排列)
	 */
	public List<N> getSubNode(N node, boolean needSelf) {
		return baseTree.getSubNode(node, this.getChildMap(), needSelf);
	}
	
	/**
	 * 获取某节点下面的所有叶子
	 * @param node	当前节点
	 * @return		叶子节点集合(如果自己是叶子,则集合中为自己)
	 */
	public List<N> getSubLeaf(N node) {
		return baseTree.getSubLeaf(node, this.getChildMap());
	}
	
	/**
	 * 获取某节点的父节点(直接上级)
	 * @param node
	 * @return		没有则返回null
	 */
	public N getParent(N node) {
		return baseTree.getParent(node, this.getSelfMap());
	}
	
	/**
	 * 获取某节点的所有孩子(直接下级)
	 * @param node
	 * @return		没有则返回null
	 */
	public List<N> getChildren(N node) {
		return baseTree.getChildren(node, this.getChildMap());
	}
	
	/**
	 * 判断一个节点是不是叶子
	 * @param node
	 * @return
	 */
	public boolean isLeaf(N node) {
		return baseTree.isLeaf(node, this.getChildMap());
	}

}

猜你喜欢

转载自blog.csdn.net/magonong/article/details/80179400