大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)
所谓树形结构,就是上级节点中,包含若干子节点,然后子节点中又包含其子节点,一般是没有层级次数限制。
数据库中又是按扁平结构存储的,因此在使用的时候,必须转换成树数据结构才行!
部门树、权限树、菜单树,好麻烦哦!一不小心就递归死循环了 (ಡωಡ)
形成树最少的条件:
- 节点必须有自己的id,并且是唯一的
id
- 节点必须有上级节点id
parentId
- 节点的
id
,和parentId
不能相同 - 节点的
parentId
,一定是属于其他节点的id
满足上述四个条件,就可以形成树了~
Nodeable
/**
* 树节点对象,必须实现这个interface,并且实现theId、theParentId方法!
* 分别返回节点的id,和父节点id
*/
public interface Nodeable {
/**
* 返回当前节点id
* */
public String theId();
/**
* 返回当前节点父节点id
* */
public String theParentId();
}
NodeInfo
import java.util.LinkedList;
import java.util.List;
public class NodeInfo<T extends Nodeable> implements Cloneable{
static String rootId = "@ROOT"; //根节点id
static String rootParentId = ""; //根节点的parent
static int rootLevel = 0; //根节点的级别
/**
* 默认根节点
* */
static NodeInfo def = new NodeInfo();
static {
def.cont = new Nodeable() {
@Override
public String theId() {
return rootId;
}
@Override
public String theParentId() {
return rootParentId;
}
};
def.level = rootLevel;
def.id = rootId;
def.parentId = rootParentId;
def.parentIds = rootParentId;
def.children = new LinkedList<>();
};
/**
* 当前元素
* */
private T cont;
private String id; //当前元素id
private String parentId; //当前元素父节点id
private List<NodeInfo<T>> children; //所有的下级节点
private int level; //从根节点开始计算,第n级节点
private String parentIds;
private String slipt = ","; //节点所有parentIds的分割符
NodeInfo(T cont) throws Exception{
if(cont == null || TreeHandler.isBlank(cont.theId())){
throw new Exception("元素或者元素id不能为空");
}
this.cont = cont;
this.id = TreeHandler.trimToEmpty(cont.theId());
this.parentId = TreeHandler.trimToEmpty(cont.theParentId());
}
private NodeInfo(){}
@Override
public NodeInfo<T> clone(){
try { return (NodeInfo<T>) super.clone(); } catch ( CloneNotSupportedException e ) { }
return null;
}
public T getCont() {
return cont;
}
public NodeInfo<T> putParent(NodeInfo<T> parent, int level, NodeInfo<T> root){
this.level = level;
this.parentId = parent.id;
this.parentIds = (parent.getParentIds() + root.slipt + parent.getId()).replaceFirst("^" + root.slipt, "");
return this;
}
public void putChild(NodeInfo<T> child){
if( children == null ){
children = new LinkedList<>();
}
children.add(child);
}
/**
* 设置分割符
* */
void setSlipt(String slipt) {
this.slipt = slipt ;
}
String getSlipt() {
return slipt;
}
public void setCont(T cont) {
this.cont = cont;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getParentIds() {
return parentIds;
}
public void setParentIds(String parentIds) {
this.parentIds = parentIds;
}
public List<NodeInfo<T>> getChildren() {
return children;
}
public void setChildren(List<NodeInfo<T>> children) {
this.children = children;
}
}
TreeHandler
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class TreeHandler<T extends Nodeable> {
/****************************************************/
private Map<String, NodeInfo<T>> nodeMap;
/**
* 根节点
* */
private NodeInfo<T> root;
private TreeHandler(){}
/**
* @param root 根节点对象
* */
public TreeHandler(NodeInfo<T> root){
this.root = root;
this.root.setId(root.getId());
this.root.setParentId(root.getParentId());
this.root.setParentIds(root.getParentIds());
this.root.setSlipt(root.getSlipt());
}
/**
* @param element 根节点元素
* @param rootParentId 根节点的上级id
* @param slipt 元素所有上级id分割符
*/
public TreeHandler(T element, String rootParentId, String slipt) throws Exception{
this.root = creatNode(element);
this.root.setId(element.theId());
this.root.setParentId(element.theParentId());
this.root.setParentIds(rootParentId);
this.root.setSlipt(slipt);
}
/**
* @param rootId 根节点的id,注意,这样会在nodeMap中存入一个虚拟的Nodeable节点,
* 在后续遍历nodes时,会可能造成类型转换异常
* */
public TreeHandler(String rootId){
this(rootId, ",");
}
/**
* @param rootId 根节点的id,注意,这样会在nodeMap中存入一个虚拟的Nodeable节点,
* 在后续遍历nodes时,会可能造成类型转换异常
* @param slipt 元素所有上级id分割符,默认","
* */
public TreeHandler(String rootId, String slipt){
this(rootId, "", slipt);
}
/**
* @param rootId 根节点的id
* @param rootParentId 根节点的上级id,默认""
* @param slipt 元素所有上级id分割符,默认","
* */
public TreeHandler(String rootId, String rootParentId, String slipt){
this.root = NodeInfo.def.clone();
this.root.setId(rootId);
this.root.setParentId(rootParentId);
this.root.setParentIds(rootParentId);
this.root.setSlipt(slipt);
}
/**
* 解析,将扁平数据解析成树状
* @param elements 待处理集合
* */
public NodeInfo<T> parseArray(List<T> elements) throws Exception{
if( TreeHandler.isNotEmpty(elements) ){
nodeMap = new HashMap<>((int)(elements.size() * 1.34));
nodeMap.put(root.getId(), root);
Map<String, List<NodeInfo<T>>> parentIdMap = this.groupByParentId(elements); //按parentId分组后的数据
List<NodeInfo<T>> firsts = parentIdMap.get(root.getId());
root.setChildren(firsts);
for( NodeInfo<T> first : firsts){
first.putParent(root, root.getLevel(), root);
parse(first, root, root.getLevel(), parentIdMap);
}
}
return root;
}
/**
* 解析
* @param curNode 当前节点
* @param parentNode 上级节点
* @param level 当前节点所在级别
* */
private void parse(NodeInfo<T> curNode, NodeInfo<T> parentNode, int level, Map<String, List<NodeInfo<T>>> parentIdMap){
level ++ ;
List<NodeInfo<T>> curNodeChildren = parentIdMap.get(curNode.getId()); //得到当前节点的子节点
curNode.putParent(parentNode, parentNode.getLevel(), root);
curNode.setChildren(curNodeChildren);
curNode.setLevel(level);
if( TreeHandler.isEmpty(curNodeChildren) ){//当前节点没有子节点了
return;
}
//当前节点有子节点
for( NodeInfo<T> child : curNodeChildren ){
parse(child, curNode, level, parentIdMap);
}
return;
}
/**
* 根据节点id,从树上,剪掉此节点、以及其后代节点
* */
public NodeInfo<T> cutNodeById(String nodeId){
NodeInfo<T> cutNode = queryTree(nodeId);
if( cutNode != null ){
NodeInfo<T> parentNode = queryTree(cutNode.getParentId());
cutNode(cutNode);
List<NodeInfo<T>> children = parentNode.getChildren();
int i = 0;
for(NodeInfo<T> child : children){
if( cutNode.getId().equals(child.getId()) ){
break;
}
i ++ ;
}
children.remove(i);
}
return cutNode;
};
/**
* 根据节点id,分离成一个子树
* */
public TreeHandler splitById(String nodeId, String rootParentId) throws Exception {
return splitById(nodeId, rootParentId, ",");
}
public TreeHandler splitById(String nodeId, String rootParentId, String slipt) throws Exception {
TreeHandler handler = new TreeHandler();
if( nodeMap.containsKey(nodeId) ){
NodeInfo<T> tree = cutNodeById(nodeId);
handler.root = tree;
handler.root.setId(tree.getId());
handler.root.setParentId(tree.getParentId());
handler.root.setParentIds(rootParentId);
handler.root.setSlipt(slipt);
List<T> elements = new LinkedList<>();
getNodes(tree, elements);
handler.root.getChildren().clear();
handler.parseArray(elements);
}
return handler;
};
private void getNodes(NodeInfo<T> node, List<T> elements){
List<NodeInfo<T>> children = node.getChildren();
if( TreeHandler.isEmpty(children) ){
elements.add(node.getCont());
return;
}
for ( NodeInfo<T> child : children ){
getNodes(child, elements);
}
elements.add(node.getCont());
}
/**
* @param node 枝剪某个节点
* */
private void cutNode(NodeInfo<T> node){
List<NodeInfo<T>> children = node.getChildren();
if( TreeHandler.isEmpty(children) ){//如果没有子节点了
nodeMap.remove(node.getId());
return;
}
for(NodeInfo<T> child : children){
cutNode(child);
}
nodeMap.remove(node.getId());
}
/**
* 得到所有的节点
* */
public List<NodeInfo<T>> getNodes(){
Collection<NodeInfo<T>> values = nodeMap.values();
List<NodeInfo<T>> list = new ArrayList<>(values.size());
for( NodeInfo<T> o : values ){
list.add(o);
}
return list;
}
/**
* 得到Map: 节点id -> 节点
* */
public Map<String, NodeInfo<T>> getNodeMap(){
return nodeMap;
}
/**
* 创建节点
* */
public NodeInfo creatNode(T element) throws Exception {
NodeInfo<T> node = null;
if( nodeMap != null ){
node = nodeMap.get(element.theId());
}
if( node == null ){
node = new NodeInfo(element);
}
//如果元素没有传入上级节点,按一级节点
if( TreeHandler.isBlank(node.getParentId()) ){
node.setParentId(root.getId());
}
return node;
}
/**
* 遍历树,并且返回该节点
* */
public NodeInfo<T> queryTree(String nodeId){
NodeInfo<T> node = nodeMap.get(nodeId);
return node;
}
/**
* 遍历,找到上级节点
* */
private NodeInfo<T> foreach(List<NodeInfo<T>> children, String seachId){
NodeInfo<T> node = null;
if( TreeHandler.isNotEmpty(children) ){
for( NodeInfo child : children ){
System.out.println(child.getId());
if( seachId.equals(child.getId()) ){
return child;
}
node = foreach(child.getChildren(), seachId);
if(node != null){
return node;
}
}
return null;
}else{
return null;
}
}
private Map<String, List<NodeInfo<T>>> groupByParentId(List<T> elements) throws Exception{
Map<String, List<NodeInfo<T>>> result = new HashMap<>((int)(elements.size() * 1.34));
for( T el : elements ){
String parent = TreeHandler.defaultIfBlank(el.theParentId(), root.getId());
if( parent.equals(el.theId()) ){
throw new Exception("元素id和parentId不能相等[id='" + el.theId() + "']");
}
List<NodeInfo<T>> group = result.get(parent);
if ( group == null ){
group = new LinkedList<>();
result.put(parent, group);
}
NodeInfo<T> node = this.creatNode(el);
group.add(node);
nodeMap.put(el.theId(), node);
}
return result;
}
public static boolean isBlank (String string) {
return string == null || "".equals(string.trim());
}
public static String trimToEmpty (String string) {
return isBlank(string) ? "" : string.trim();
}
private static String defaultIfBlank (String string, String defaultValue) {
return isBlank(string) ? defaultValue : string;
}
public static boolean isEmpty (List<?> list) {
return list == null || list.isEmpty();
}
public static boolean isNotEmpty (List<?> list) {
return !isEmpty(list);
}
}
喜闻乐见的呆毛Demo
import java.util.ArrayList;
import java.util.List;
/**
* 任意一个对象,实现Nodeable接口
* 返回id,和父节点id
*/
public class Demo implements Nodeable {
private String id;
private String pid;
private String name;
public Demo(String id, String pid){
this.id = id;
this.pid = pid;
}
@Override
public String theId() {
return id;
}
@Override
public String theParentId() {
return pid;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
useroot
10
120
4876
41344
4582
45664
58761
125
451
454678
64487
510345
115
11
153
186
178
13
137
15130
5131
158
1783
1568
136
*/
public static void main(String[] args) throws Exception{
List<Demo> list = new ArrayList();
list.add(new Demo("10", ""));
list.add(new Demo("120", "10"));
list.add(new Demo("4876", "120"));
list.add(new Demo("41344", "4876"));
list.add(new Demo("4582", "120"));
list.add(new Demo("45664", "4582"));
list.add(new Demo("58761", "4582"));
list.add(new Demo("125", "10"));
list.add(new Demo("451", "125"));
list.add(new Demo("454678", "451"));
list.add(new Demo("64487", "454678"));
list.add(new Demo("510345", "64487"));
list.add(new Demo("115", "10"));
list.add(new Demo("11", ""));
list.add(new Demo("153", "11"));
list.add(new Demo("186", "11"));
list.add(new Demo("178", "11"));
list.add(new Demo("13", ""));
list.add(new Demo("137", "13"));
list.add(new Demo("15130", "137"));
list.add(new Demo("5131", "15130"));
list.add(new Demo("158", "13"));
list.add(new Demo("1783", "158"));
list.add(new Demo("1568", "158"));
list.add(new Demo("136", "13"));
TreeHandler<Demo> tree = new TreeHandler<>("useroot");
NodeInfo<Demo> root = tree.parseArray(list);
NodeInfo<Demo> childTree = tree.queryTree("4582");
System.out.println(childTree.getParentIds());
List<NodeInfo<Demo>> nodes = tree.getNodes();
TreeHandler handler = tree.splitById("13", "");
handler.getNodeMap();
}
}
就酱~
乾杯 []( ̄▽ ̄)*
2018-11-10 plus版,优化递归遍历;增加分割子树