From d05fd5af8de8362cc7dcf9eef2e9d1b268704fe1 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 18 Jun 2021 18:12:36 +0800 Subject: [PATCH] add TreeBuilder --- CHANGELOG.md | 1 + .../cn/hutool/core/collection/IterUtil.java | 35 ++++ .../cn/hutool/core/lang/tree/TreeBuilder.java | 149 ++++++++++++++++++ .../cn/hutool/core/lang/tree/TreeUtil.java | 126 ++++++++++----- 4 files changed, 269 insertions(+), 42 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeBuilder.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 989dff586..5cbef91ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * 【core 】 增加UserPassAuthenticator * 【db 】 获取分组数据源时,移除公共属性项 * 【core 】 增加StrJoiner +* 【core 】 增加TreeBuilder ### 🐞Bug修复 * 【db 】 修复Oracle下别名错误造成的SQL语法啊错误(issue#I3VTQW@Gitee) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java index 5efa92e63..81caf172d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java @@ -563,6 +563,21 @@ public class IterUtil { return getFirst(iterable.iterator()); } + /** + * 获取集合的第一个非空元素 + * + * @param 集合元素类型 + * @param iterable {@link Iterable} + * @return 第一个元素 + * @since 5.7.2 + */ + public static T getFirstNoneNull(Iterable iterable) { + if (null == iterable) { + return null; + } + return getFirstNoneNull(iterable.iterator()); + } + /** * 获取集合的第一个元素 * @@ -577,6 +592,26 @@ public class IterUtil { return null; } + /** + * 获取集合的第一个非空元素 + * + * @param 集合元素类型 + * @param iterator {@link Iterator} + * @return 第一个非空元素,null表示未找到 + * @since 5.7.2 + */ + public static T getFirstNoneNull(Iterator iterator) { + if (null != iterator) { + while(iterator.hasNext()){ + final T next = iterator.next(); + if(null != next){ + return next; + } + } + } + return null; + } + /** * 获得{@link Iterable}对象的元素类型(通过第一个非空元素判断)
* 注意,此方法至少会调用多次next方法 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeBuilder.java b/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeBuilder.java new file mode 100644 index 000000000..b1e6a9814 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/tree/TreeBuilder.java @@ -0,0 +1,149 @@ +package cn.hutool.core.lang.tree; + +import cn.hutool.core.builder.Builder; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.parser.NodeParser; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 树构建器 + * + * @param ID类型 + */ +public class TreeBuilder implements Builder> { + private static final long serialVersionUID = 1L; + + private final Tree root; + private final Map> idTreeMap; + + /** + * 创建Tree构建器 + * + * @param rootId 根节点ID + * @param ID类型 + * @return {@link TreeBuilder} + */ + public static TreeBuilder of(T rootId) { + return of(rootId, null); + } + + /** + * 创建Tree构建器 + * + * @param rootId 根节点ID + * @param config 配置 + * @param ID类型 + * @return {@link TreeBuilder} + */ + public static TreeBuilder of(T rootId, TreeNodeConfig config) { + return new TreeBuilder<>(rootId, config); + } + + /** + * 构造 + * + * @param rootId 根节点ID + * @param config 配置 + */ + public TreeBuilder(E rootId, TreeNodeConfig config) { + root = new Tree<>(config); + root.setId(rootId); + this.idTreeMap = new HashMap<>(); + } + + /** + * 增加节点列表,增加的节点是不带子节点的 + * + * @param map 节点列表 + * @return this + */ + public TreeBuilder append(Map> map) { + this.idTreeMap.putAll(map); + return this; + } + + /** + * 增加节点列表,增加的节点是不带子节点的 + * + * @param trees 节点列表 + * @return this + */ + public TreeBuilder append(Iterable> trees) { + for (Tree tree : trees) { + this.idTreeMap.put(tree.getId(), tree); + } + return this; + } + + /** + * 增加节点列表,增加的节点是不带子节点的 + * + * @param list Bean列表 + * @param Bean类型 + * @param nodeParser 节点转换器,用于定义一个Bean如何转换为Tree节点 + * @return this + */ + public TreeBuilder append(List list, NodeParser nodeParser) { + final TreeNodeConfig config = this.root.getConfig(); + final Map> map = new LinkedHashMap<>(list.size(), 1); + Tree node; + for (T t : list) { + node = new Tree<>(config); + nodeParser.parse(t, node); + map.put(node.getId(), node); + } + return append(map); + } + + @Override + public Tree build() { + buildFromMap(); + return root; + } + + /** + * 构建树列表,例如: + * + *
+	 * -用户管理
+	 *  --用户添加
+	 *  --用户管理
+	 * - 部门管理
+	 *  --部门添加
+	 *  --部门管理
+	 * 
+ * + * @return 树列表 + */ + public List> buildList() { + return this.root.getChildren(); + } + + /** + * 开始构建 + */ + private void buildFromMap() { + final Map> eTreeMap = MapUtil.sortByValue(this.idTreeMap, false); + List> rootTreeList = CollUtil.newArrayList(); + E parentId; + for (Tree node : eTreeMap.values()) { + parentId = node.getParentId(); + if (ObjectUtil.equals(this.root.getId(), parentId)) { + this.root.addChildren(node); + rootTreeList.add(node); + continue; + } + + final Tree parentNode = eTreeMap.get(parentId); + if (null != parentNode) { + parentNode.addChildren(node); + } + } + } +} 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 85063c779..36c487bc1 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 @@ -1,13 +1,11 @@ package cn.hutool.core.lang.tree; -import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.IterUtil; 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.Map; @@ -18,6 +16,17 @@ import java.util.Map; */ public class TreeUtil { + /** + * 构建单root节点树 + * + * @param list 源数据集合 + * @return List + * @since 5.7.2 + */ + public static Tree buildSingle(List> list) { + return buildSingle(list, 0); + } + /** * 树构建 * @@ -28,6 +37,19 @@ public class TreeUtil { return build(list, 0); } + /** + * 构建单root节点树 + * + * @param ID类型 + * @param list 源数据集合 + * @param parentId 最顶层父id值 一般为 0 之类 + * @return List + * @since 5.7.2 + */ + public static Tree buildSingle(List> list, E parentId) { + return buildSingle(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, new DefaultNodeParser<>()); + } + /** * 树构建 * @@ -40,6 +62,21 @@ public class TreeUtil { return build(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, new DefaultNodeParser<>()); } + /** + * 构建单root节点树 + * + * @param 转换的实体 为数据源里的对象类型 + * @param ID类型 + * @param list 源数据集合 + * @param parentId 最顶层父id值 一般为 0 之类 + * @param nodeParser 转换器 + * @return List + * @since 5.7.2 + */ + public static Tree buildSingle(List list, E parentId, NodeParser nodeParser) { + return buildSingle(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, nodeParser); + } + /** * 树构建 * @@ -66,37 +103,24 @@ public class TreeUtil { * @return List */ 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); - } - - return build(map, rootId); + return buildSingle(list, rootId, treeNodeConfig, nodeParser).getChildren(); } /** - * 单点树构建,按照权重排序 + * 构建单root节点树 * - * @param ID类型 - * @param map 源数据Map - * @param rootId 根节点id值 一般为 0 之类 - * @return {@link Tree} + * @param 转换的实体 为数据源里的对象类型 + * @param ID类型 + * @param list 源数据集合 + * @param rootId 最顶层父id值 一般为 0 之类 + * @param treeNodeConfig 配置 + * @param nodeParser 转换器 + * @return List * @since 5.7.2 */ - public static Tree buildSingle(Map> map, E rootId) { - final List> list = build(map, rootId); - if (CollUtil.isNotEmpty(list)) { - final TreeNodeConfig config = list.get(0).getConfig(); - final Tree root = new Tree<>(config); - root.setId(rootId); - root.setChildren(list); - return root; - } - - return new Tree(null).setId(rootId); + public static Tree buildSingle(List list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser nodeParser) { + return TreeBuilder.of(rootId, treeNodeConfig) + .append(list, nodeParser).build(); } /** @@ -109,22 +133,28 @@ public class TreeUtil { * @since 5.6.7 */ 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; - } + return buildSingle(map, rootId).getChildren(); + } - final Tree parentNode = map.get(parentId); - if (null != parentNode) { - parentNode.addChildren(node); - } + /** + * 单点树构建,按照权重排序 + * + * @param ID类型 + * @param map 源数据Map + * @param rootId 根节点id值 一般为 0 之类 + * @return {@link Tree} + * @since 5.7.2 + */ + public static Tree buildSingle(Map> map, E rootId) { + final Tree tree = IterUtil.getFirstNoneNull(map.values()); + if (null != tree) { + final TreeNodeConfig config = tree.getConfig(); + return TreeBuilder.of(rootId, config) + .append(map) + .build(); } - return rootTreeList; + + return createEmptyNode(rootId); } /** @@ -190,4 +220,16 @@ public class TreeUtil { } return result; } + + /** + * 创建空Tree的节点 + * + * @param id 节点ID + * @param 节点ID类型 + * @return {@link Tree} + * @since 5.7.2 + */ + public static Tree createEmptyNode(E id) { + return new Tree().setId(id); + } }