在项目开发过程中,我们经常会遇到树形数据结构的设计,例如菜单树,地区树和类别树等等。一般而言,我们需要把数据库中记录全取出来,然后构建树(注意的是,最好是一次性取出来,如果是ajax按需拉数据则不需要)。下面分享了递归和非递归两种方式:
package test.tree;
import java.util.ArrayList;
import java.util.List;
public class Node {
private String id;
private String parentId;
private String value;
private Node parent;
private List<Node> children;
public Node() {
super();
this.children = new ArrayList<>();
}
public Node(String value, String id, String parentId) {
this.value = value;
this.id = id;
this.parentId = parentId;
this.children = new ArrayList<>();
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
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 Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public List<Node> getChildren() {
return children;
}
public void setChildren(List<Node> children) {
this.children = children;
}
public void addChild(Node child) {
if (!this.children.contains(child) && child != null)
this.children.add(child);
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj instanceof Node) {
if(this.id.equals(((Node) obj).getId())) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "Node [id=" + id + ", parentId=" + parentId + ", value=" + value + ", children=" + children + "]";
}
}
非递归的方式
package test.tree;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ListToTreeNoRecurse {
public static void main(String[] args) throws IOException {
List<Node> nodes = new ArrayList<>();
List<String> lines = Files
.readAllLines(Paths.get("C:\\Users\\95\\Desktop\\leetCode\\src\\test\\tree\\jsontest.txt"));
nodes.add(new Node("all", "0", null));
for (String line : lines) {
String[] values = line.split(",");
Node node = new Node(values[1].trim(), values[0].trim(), values[3].trim());
nodes.add(node);
}
long start = System.currentTimeMillis();
createTree(nodes);
long end = System.currentTimeMillis();
System.err.println((end - start));
}
private static void createTree(List<Node> nodes) {
Map<String, Node> mapTmp = new HashMap<>();
// Save all nodes to a map
for (Node current : nodes) {
mapTmp.put(current.getId(), current);
}
// loop and assign parent/child relationships
for (Node current : nodes) {
String parentId = current.getParentId();
if (parentId != null) {
Node parent = mapTmp.get(parentId);
if (parent != null) {
current.setParent(parent);
parent.addChild(current);
mapTmp.put(parentId, parent);
mapTmp.put(current.getId(), current);
}
}
}
System.out.println(mapTmp.get("0"));
}
}
递归的方法来
package test.tree;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import test.tree.Node;
public class TreeBuilder {
/**
* 使用递归方法建树
* @param Nodes
* @return
*/
public static List<Node> buildByRecursive(List<Node> Nodes) {
List<Node> trees = new ArrayList<Node>();
for (Node Node : Nodes) {
if ("0".equals(Node.getParentId())) {
trees.add(findChildren(Node,Nodes));
}
}
return trees;
}
/**
* 递归查找子节点
* @param Nodes
* @return
*/
public static Node findChildren(Node Node,List<Node> Nodes) {
for (Node it : Nodes) {
if(Node.getId().equals(it.getParentId())) {
if (Node.getChildren() == null) {
Node.setChildren(new ArrayList<Node>());
}
Node.getChildren().add(findChildren(it,Nodes));
}
}
return Node;
}
public static void main(String[] args) throws IOException {
List<Node> list = new ArrayList<Node>();
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\95\\Desktop\\leetCode\\src\\test\\tree\\jsontest.txt"));
for(String line:lines) {
String[] values = line.split(",");
Node node = new Node(values[1], values[0], values[3]);
list.add(node);
}
long beforeUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
long start = System.currentTimeMillis();
List<Node> trees_ = TreeBuilder.buildByRecursive(list);
long afterUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
long actualMemUsed=afterUsedMem-beforeUsedMem;
long end = System.currentTimeMillis();
System.out.println(trees_);
System.err.println((end - start));
System.err.println(actualMemUsed);
}
}
上述方法的测试数据是世界地区数据,下载地址是:https://epubi.oss-cn-shanghai.aliyuncs.com/jsontest.txt
经过测试,如果不打印树的话,非递归方法构建树是非常快的,远少于递归方法。打印树的话,非递归的方法的性能是略微优于递归的。
因为非递归方法构建的时候,只是构建child,parent之间的引用,在真正打印的时候,会调用toString方法对child进行打印,这个时候也是一个遍历的过程。在项目中还是推荐使用非递归方法,非递归方法比较可控。
该方法继承至AbstractCollection中的toString方法。
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}