在项目开发过程中,我们经常会遇到树形数据结构的设计,例如菜单树,地区树和类别树等等。一般而言,我们需要把数据库中记录全取出来,然后构建树(注意的是,最好是一次性取出来,如果是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(' '); } }