import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
/**
* 描述:简单树型结构,一定要正确构建,否则容易陷入死循环
*
* @author jianlin.zhu
* @create 2019-03-28 13:49
**/
public class Tree<K,T> implements Serializable {
private K key;
private T data;
private List<Tree<K, T>> children = new ArrayList<>();
private K parent;
private int level;
public Tree(K key, T data) {
this.key = key;
this.data = data;
this.parent = null;
}
public Tree(K key, T data, K parent, int level) {
this.key = key;
this.data = data;
this.parent = parent;
this.level = level;
}
public boolean isRoot() {
return parent == null;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public List<Tree<K, T>> getChildren() {
return children;
}
public void setChildren(List<Tree<K, T>> children) {
this.children = children;
}
public K getParent() {
return parent;
}
public void setParent(K parent) {
this.parent = parent;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
/**
* 是否为叶子节点,根节点不算
* @return
*/
public boolean isLeaf() {
return !isRoot() && this.getChildren().size()==0;
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
public Tree<K,T> findByKey(K key){
Tree<K,T> root = this;
if(root.key.equals(key)){
return root;
}
if(root.getChildren().size()>0){
for(Tree<K,T> child : root.getChildren() ){
Tree<K,T> a = child.findByKey(key);
if(a != null){
return a;
}
}
}
return null;
}
public List<Tree<K,T> > asList(){
List<Tree<K,T>> trees = new ArrayList<>();
Tree<K,T> root = this;
trees.add(root);
for(Tree<K,T> child : root.getChildren() ){
trees.addAll(child.asList());
}
return trees;
}
public List<Tree<K,T> > asListWithoutChildren(){
List<Tree<K,T> > result = new ArrayList<>();
List<Tree<K,T> > list = asList();
for(Tree<K,T> item:list){
result.add(new Tree<>(item.getKey(),item.getData(),item.getParent(),item.getLevel()));
}
return result;
}
public Tree<K,T> removeNode(K key){
Tree<K,T> _node = null;
if( (_node = findByKey(key))!=null ){
if(_node.getParent() != null){
Tree<K,T> parent = findByKey(_node.getParent());
if(parent != null){
if(parent.getChildren().remove(_node)){
return _node;
}
}
}
}
return _node;
}
public static <K, T> Tree<K, T> buildFrom(List<Tree<K, T>> list) {
Tree<K, T> root = null;
for (Tree<K, T> node : list) {
if(node.getParent() == null){
root = node;
break;
}
}
for (Tree<K, T> node : list) {
if(node.getParent() == null){
continue;
}
Tree<K, T> parent = root.findByKey(node.getParent());
parent.getChildren().add(node);
}
return root;
}
/**
* 后根顺遍历
* @param tree
* @param visitor
* @param <K>
* @param <T>
*/
public static <K, T> List<Object> postOrder(Tree<K, T> tree, Visitor<K,T> visitor){
List<Object> result = new ArrayList<>();
if(tree != null){
result.addAll(postOrder(tree.getChildren().size()==0?null:tree.getChildren().get(0),visitor));// left
if(tree.getChildren().size()>1){ //right
for(int i=1;i<tree.getChildren().size();++i){
result.addAll(postOrder(tree.getChildren().get(i),visitor));
}
}
// visit root
Object obj= visitor.handle(tree.getKey(),tree.getData(),tree.getParent(),tree.getLevel(),tree);
if(obj !=null ){
result.add(obj);
}
}
return result;
}
}
public interface Visitor<K,T> {
Object handle(K key, T data, K parent, int level,Tree<K, T> cur);
}
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import org.junit.Test;
public class TreeTest extends FastBaseTest{
@Test
public void test(){
Long ROOT_KEY = 0L;
Tree<Long, MenuVO> root = new Tree<>(ROOT_KEY, null, null, 0);
Tree<Long, MenuVO> first1 = new Tree<>(1L, new MenuVO(1L,"/page1","",0L,1,1), ROOT_KEY, 1);
Tree<Long, MenuVO> first2 = new Tree<>(2L, new MenuVO(2L,"/page2","",0L,2,1), ROOT_KEY, 1);
Tree<Long, MenuVO> second1 = new Tree<>(21L, new MenuVO(11L,"/page1/init","",1L,1,1), 1L, 1);
Tree<Long, MenuVO> second2 = new Tree<>(22L, new MenuVO(12L,"/page1/main","",1L,1,1), 1L, 1);
Tree<Long, MenuVO> myTree = Tree.buildFrom(Lists.newArrayList(root,first1,first2,second1,second2));
// 等价于
// root.getChildren().add(first1);
// root.getChildren().add(first2);
// first1.getChildren().add(second1);
// first1.getChildren().add(second2);
print(myTree,true);
// 打印每一个节点
myTree.asListWithoutChildren().forEach(a->{
print(a,true);
});
// 序列化树
String treeStr = JSONObject.toJSONString(myTree);
print(treeStr,true);
// 反序列化树,还不能还原!
Tree<Long,MenuVO> tree = JSONObject.parseObject(treeStr,Tree.class);
print(tree,true);
}
public void testPostOrder( Tree<Long, MenuVO> root){
List<Object> result = Tree.postOrder(root, new Visitor<Long, MenuVO>() {
@Override
public Object handle(Long aLong, MenuVO data,Long parent,int level,Tree<Long, MenuVO> cur) {
System.out.println(data==null?"root":data.getUri());
return data==null?"root":data.getUri();
}
});
String treeStr = JSONObject.toJSONString(result);
print(treeStr,true);
}
}
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
public class FastBaseTest {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected void print(Object obj, boolean isPretty, Object... params) {
logger.info(JSONObject.toJSONString(obj, isPretty));
if (params != null) {
for (Object param : params) {
logger.info(JSONObject.toJSONString(param, isPretty));
}
}
}
public static void waitClose(ExecutorService executorService) {
executorService.shutdown();
while(!executorService.isTerminated()){
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MenuVO implements Serializable {
private Long id;
private String uri;
private String name;
private Long pid;
private Integer order;
private Integer level;
}