enhance TreeUtil.build

This commit is contained in:
Looly 2021-06-04 21:51:20 +08:00
parent 4d7b4da46a
commit e7fb9759f3
4 changed files with 94 additions and 51 deletions

View File

@ -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

View File

@ -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<T> extends LinkedHashMap<String, Object> implements Node<T> {
return (List<Tree<T>>) this.get(treeNodeConfig.getChildrenKey());
}
public void setChildren(List<Tree<T>> children) {
public Tree<T> setChildren(List<Tree<T>> children) {
this.put(treeNodeConfig.getChildrenKey(), children);
return this;
}
/**
* 增加子节点同时关联子节点的父节点为当前节点
*
* @param children 子节点列表
* @return this
* @since 5.6.7
*/
@SafeVarargs
public final Tree<T> addChildren(Tree<T>... children){
if(ArrayUtil.isNotEmpty(children)){
List<Tree<T>> childrenList = this.getChildren();
if(null == childrenList){
childrenList = new ArrayList<>();
setChildren(childrenList);
}
for (Tree<T> child : children) {
child.setParent(this);
childrenList.add(child);
}
}
return this;
}
/**
@ -166,4 +197,29 @@ public class Tree<T> extends LinkedHashMap<String, Object> implements Node<T> {
Assert.notEmpty(key, "Key must be not empty !");
this.put(key, value);
}
}
@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<? extends Tree<?>> children = tree.getChildren();
if(CollUtil.isNotEmpty(children)){
for (Tree<?> child : children) {
printTree(child, writer, intent + 2);
}
}
}
}

View File

@ -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 <T> 转换的实体 为数据源里的对象类型
* @param <E> ID类型
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @param rootId 最顶层父id值 一般为 0 之类
* @param treeNodeConfig 配置
* @param nodeParser 转换器
* @return List
*/
public static <T, E> List<Tree<E>> build(List<T> list, E parentId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
final List<Tree<E>> treeList = CollUtil.newArrayList();
Tree<E> tree;
for (T obj : list) {
tree = new Tree<>(treeNodeConfig);
nodeParser.parse(obj, tree);
treeList.add(tree);
public static <T, E> List<Tree<E>> build(List<T> list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
final Map<E, Tree<E>> map = new LinkedHashMap<>(list.size(), 1);
Tree<E> node;
for (T t : list) {
node = new Tree<>(treeNodeConfig);
nodeParser.parse(t, node);
map.put(node.getId(), node);
}
List<Tree<E>> finalTreeList = CollUtil.newArrayList();
for (Tree<E> 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 <E> ID类型
* @param map 源数据Map
* @param rootId 最顶层父id值 一般为 0 之类
* @return List
* @since 5.6.7
*/
private static <T> void innerBuild(List<Tree<T>> treeNodes, Tree<T> parentNode, int deep, Integer maxDeep) {
public static <E> List<Tree<E>> build(Map<E, Tree<E>> map, E rootId) {
final Map<E, Tree<E>> eTreeMap = MapUtil.sortByValue(map, false);
List<Tree<E>> rootTreeList = CollUtil.newArrayList();
E parentId;
for (Tree<E> 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<T> childNode : treeNodes) {
if (parentNode.getId().equals(childNode.getParentId())) {
List<Tree<T>> 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<E> 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<T> childNode;
for (Tree<T> child : children) {
@ -182,5 +168,4 @@ public class TreeUtil {
}
return result;
}
}

View File

@ -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<String> tree : treeList) {
Assert.assertNotNull(tree);
Assert.assertEquals("0", tree.getParentId());
// Console.log(tree);
}
// 测试通过子节点查找父节点