大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)
所谓树形结构,就是上级节点中,包含若干子节点,然后子节点中又包含其子节点,一般是没有层级次数限制。
数据库中又是按扁平结构存储的,因此在使用的时候,必须转换成树数据结构才行!
部门树、权限树、菜单树,好麻烦哦!一不小心就递归死循环了 (ಡωಡ)
形成树最少的条件:
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 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> 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 clone(){
try { return (NodeInfo) super.clone(); } catch ( CloneNotSupportedException e ) { }
return null;
}
public T getCont() {
return cont;
}
public NodeInfo putParent(NodeInfo parent, int level, NodeInfo 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 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> getChildren() {
return children;
}
public void setChildren(List> 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 {
/****************************************************/
private Map> nodeMap;
/**
* 根节点
* */
private NodeInfo root;
private TreeHandler(){}
/**
* @param root 根节点对象
* */
public TreeHandler(NodeInfo 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 parseArray(List elements) throws Exception{
if( TreeHandler.isNotEmpty(elements) ){
nodeMap = new HashMap<>((int)(elements.size() * 1.34));
nodeMap.put(root.getId(), root);
Map>> parentIdMap = this.groupByParentId(elements); //按parentId分组后的数据
List> firsts = parentIdMap.get(root.getId());
root.setChildren(firsts);
for( NodeInfo 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 curNode, NodeInfo parentNode, int level, Map>> parentIdMap){
level ++ ;
List> curNodeChildren = parentIdMap.get(curNode.getId()); //得到当前节点的子节点
curNode.putParent(parentNode, parentNode.getLevel(), root);
curNode.setChildren(curNodeChildren);
curNode.setLevel(level);
if( TreeHandler.isEmpty(curNodeChildren) ){//当前节点没有子节点了
return;
}
//当前节点有子节点
for( NodeInfo child : curNodeChildren ){
parse(child, curNode, level, parentIdMap);
}
return;
}
/**
* 根据节点id,从树上,剪掉此节点、以及其后代节点
* */
public NodeInfo cutNodeById(String nodeId){
NodeInfo cutNode = queryTree(nodeId);
if( cutNode != null ){
NodeInfo parentNode = queryTree(cutNode.getParentId());
cutNode(cutNode);
List> children = parentNode.getChildren();
int i = 0;
for(NodeInfo 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 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 elements = new LinkedList<>();
getNodes(tree, elements);
handler.root.getChildren().clear();
handler.parseArray(elements);
}
return handler;
};
private void getNodes(NodeInfo node, List elements){
List> children = node.getChildren();
if( TreeHandler.isEmpty(children) ){
elements.add(node.getCont());
return;
}
for ( NodeInfo child : children ){
getNodes(child, elements);
}
elements.add(node.getCont());
}
/**
* @param node 枝剪某个节点
* */
private void cutNode(NodeInfo node){
List> children = node.getChildren();
if( TreeHandler.isEmpty(children) ){//如果没有子节点了
nodeMap.remove(node.getId());
return;
}
for(NodeInfo child : children){
cutNode(child);
}
nodeMap.remove(node.getId());
}
/**
* 得到所有的节点
* */
public List> getNodes(){
Collection> values = nodeMap.values();
List> list = new ArrayList<>(values.size());
for( NodeInfo o : values ){
list.add(o);
}
return list;
}
/**
* 得到Map: 节点id -> 节点
* */
public Map> getNodeMap(){
return nodeMap;
}
/**
* 创建节点
* */
public NodeInfo creatNode(T element) throws Exception {
NodeInfo 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 queryTree(String nodeId){
NodeInfo node = nodeMap.get(nodeId);
return node;
}
/**
* 遍历,找到上级节点
* */
private NodeInfo foreach(List> children, String seachId){
NodeInfo 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>> groupByParentId(List elements) throws Exception{
Map>> 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> group = result.get(parent);
if ( group == null ){
group = new LinkedList<>();
result.put(parent, group);
}
NodeInfo 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 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 tree = new TreeHandler<>("useroot");
NodeInfo root = tree.parseArray(list);
NodeInfo childTree = tree.queryTree("4582");
System.out.println(childTree.getParentIds());
List> nodes = tree.getNodes();
TreeHandler handler = tree.splitById("13", "");
handler.getNodeMap();
}
}
就酱~
乾杯 []( ̄▽ ̄)*
2018-11-10 plus版,优化递归遍历;增加分割子树