在項目開發過程當中,咱們常常會遇到樹形數據結構的設計,例如菜單樹,地區樹和類別樹等等。通常而言,咱們須要把數據庫中記錄全取出來,而後構建樹(注意的是,最好是一次性取出來,若是是ajax按需拉數據則不須要)。下面分享了遞歸和非遞歸兩種方式:java
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 + "]"; } }
非遞歸的方式node
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")); } }
遞歸的方法來ajax
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數據庫
通過測試,若是不打印樹的話,非遞歸方法構建樹是很是快的,遠少於遞歸方法。打印樹的話,非遞歸的方法的性能是略微優於遞歸的。json
由於非遞歸方法構建的時候,只是構建child,parent之間的引用,在真正打印的時候,會調用toString方法對child進行打印,這個時候也是一個遍歷的過程。在項目中仍是推薦使用非遞歸方法,非遞歸方法比較可控。數據結構
該方法繼承至AbstractCollection中的toString方法。app
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(' '); } }