From e7fb9759f33ca06458541dc7612b5bad2db062b1 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 4 Jun 2021 21:51:20 +0800 Subject: [PATCH] enhance TreeUtil.build --- CHANGELOG.md | 1 + .../java/cn/hutool/core/lang/tree/Tree.java | 60 +++++++++++++- .../cn/hutool/core/lang/tree/TreeUtil.java | 81 ++++++++----------- .../cn/hutool/core/lang/tree/TreeTest.java | 3 +- 4 files changed, 94 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed571b6a1..4f545dc91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * 【core 】 CharSequenceUtil增加join重载(issue#I3TFJ5@Gitee) * 【http 】 HttpRequest增加form方法重载(pr#337@Gitee) * 【http 】 ImgUtil增加getMainColor方法(pr#338@Gitee) +* 【core 】 改进TreeUtil.buid算法性能(pr#1594@Github) ### 🐞Bug修复 * 【core 】 修复FileUtil.normalize去掉末尾空格问题(issue#1603@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/tree/Tree.java b/hutool-core/src/main/java/cn/hutool/core/lang/tree/Tree.java index c33e63873..30eac9adf 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/tree/Tree.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/tree/Tree.java @@ -1,8 +1,15 @@ package cn.hutool.core.lang.tree; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -152,8 +159,32 @@ public class Tree extends LinkedHashMap implements Node { return (List>) this.get(treeNodeConfig.getChildrenKey()); } - public void setChildren(List> children) { + public Tree setChildren(List> children) { this.put(treeNodeConfig.getChildrenKey(), children); + return this; + } + + /** + * 增加子节点,同时关联子节点的父节点为当前节点 + * + * @param children 子节点列表 + * @return this + * @since 5.6.7 + */ + @SafeVarargs + public final Tree addChildren(Tree... children){ + if(ArrayUtil.isNotEmpty(children)){ + List> childrenList = this.getChildren(); + if(null == childrenList){ + childrenList = new ArrayList<>(); + setChildren(childrenList); + } + for (Tree child : children) { + child.setParent(this); + childrenList.add(child); + } + } + return this; } /** @@ -166,4 +197,29 @@ public class Tree extends LinkedHashMap implements Node { Assert.notEmpty(key, "Key must be not empty !"); this.put(key, value); } -} \ No newline at end of file + + @Override + public String toString() { + final StringWriter stringWriter = new StringWriter(); + printTree(this, new PrintWriter(stringWriter), 0); + return stringWriter.toString(); + } + + /** + * 打印 + * @param tree 树 + * @param writer Writer + * @param intent 缩进量 + */ + private static void printTree(Tree tree, PrintWriter writer, int intent){ + writer.println(StrUtil.format("{}{}[{}]", StrUtil.repeat(CharUtil.SPACE, intent), tree.getName(), tree.getId())); + writer.flush(); + + final List> children = tree.getChildren(); + if(CollUtil.isNotEmpty(children)){ + for (Tree child : children) { + printTree(child, writer, intent + 2); + } + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeUtil.java index 1babcc1fb..45ada39ad 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeUtil.java @@ -3,11 +3,13 @@ package cn.hutool.core.lang.tree; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.tree.parser.DefaultNodeParser; import cn.hutool.core.lang.tree.parser.NodeParser; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; /** * 树工具类 @@ -58,65 +60,49 @@ public class TreeUtil { * @param 转换的实体 为数据源里的对象类型 * @param ID类型 * @param list 源数据集合 - * @param parentId 最顶层父id值 一般为 0 之类 + * @param rootId 最顶层父id值 一般为 0 之类 * @param treeNodeConfig 配置 * @param nodeParser 转换器 * @return List */ - public static List> build(List list, E parentId, TreeNodeConfig treeNodeConfig, NodeParser nodeParser) { - final List> treeList = CollUtil.newArrayList(); - Tree tree; - for (T obj : list) { - tree = new Tree<>(treeNodeConfig); - nodeParser.parse(obj, tree); - treeList.add(tree); + public static List> build(List list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser nodeParser) { + final Map> map = new LinkedHashMap<>(list.size(), 1); + Tree node; + for (T t : list) { + node = new Tree<>(treeNodeConfig); + nodeParser.parse(t, node); + map.put(node.getId(), node); } - List> finalTreeList = CollUtil.newArrayList(); - for (Tree node : treeList) { - if (ObjectUtil.equals(parentId,node.getParentId())) { - finalTreeList.add(node); - innerBuild(treeList, node, 0, treeNodeConfig.getDeep()); - } - } - // 内存每层已经排过了 这是最外层排序 - finalTreeList = finalTreeList.stream().sorted().collect(Collectors.toList()); - return finalTreeList; + return build(map, rootId); } /** - * 递归处理 + * 树构建,按照权重排序 * - * @param treeNodes 数据集合 - * @param parentNode 当前节点 - * @param deep 已递归深度 - * @param maxDeep 最大递归深度 可能为null即不限制 + * @param ID类型 + * @param map 源数据Map + * @param rootId 最顶层父id值 一般为 0 之类 + * @return List + * @since 5.6.7 */ - private static void innerBuild(List> treeNodes, Tree parentNode, int deep, Integer maxDeep) { + public static List> build(Map> map, E rootId) { + final Map> eTreeMap = MapUtil.sortByValue(map, false); + List> rootTreeList = CollUtil.newArrayList(); + E parentId; + for (Tree node : eTreeMap.values()) { + parentId = node.getParentId(); + if(ObjectUtil.equals(rootId, parentId)){ + rootTreeList.add(node); + continue; + } - if (CollUtil.isEmpty(treeNodes)) { - return; - } - //maxDeep 可能为空 - if (maxDeep != null && deep >= maxDeep) { - return; - } - - // 每层排序 TreeNodeMap 实现了Comparable接口 - treeNodes = treeNodes.stream().sorted().collect(Collectors.toList()); - for (Tree childNode : treeNodes) { - if (parentNode.getId().equals(childNode.getParentId())) { - List> children = parentNode.getChildren(); - if (children == null) { - children = CollUtil.newArrayList(); - parentNode.setChildren(children); - } - children.add(childNode); -// childNode.setParentId(parentNode.getId()); - childNode.setParent(parentNode); - innerBuild(treeNodes, childNode, deep + 1, maxDeep); + final Tree parentNode = map.get(parentId); + if(null != parentNode){ + parentNode.addChildren(node); } } + return rootTreeList; } /** @@ -138,7 +124,7 @@ public class TreeUtil { if(null == children) { return null; } - + // 查找子节点 Tree childNode; for (Tree child : children) { @@ -182,5 +168,4 @@ public class TreeUtil { } return result; } - } diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/tree/TreeTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/tree/TreeTest.java index a977451a8..6202224bb 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/tree/TreeTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/tree/TreeTest.java @@ -18,8 +18,8 @@ public class TreeTest { static { // 模拟数据 nodeList.add(new TreeNode<>("1", "0", "系统管理", 5)); - nodeList.add(new TreeNode<>("11", "1", "用户管理", 222222)); nodeList.add(new TreeNode<>("111", "11", "用户添加", 0)); + nodeList.add(new TreeNode<>("11", "1", "用户管理", 222222)); nodeList.add(new TreeNode<>("2", "0", "店铺管理", 1)); nodeList.add(new TreeNode<>("21", "2", "商品管理", 44)); @@ -33,6 +33,7 @@ public class TreeTest { for (Tree tree : treeList) { Assert.assertNotNull(tree); Assert.assertEquals("0", tree.getParentId()); +// Console.log(tree); } // 测试通过子节点查找父节点