mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
enhance TreeUtil.build
This commit is contained in:
parent
4d7b4da46a
commit
e7fb9759f3
@ -9,6 +9,7 @@
|
|||||||
* 【core 】 CharSequenceUtil增加join重载(issue#I3TFJ5@Gitee)
|
* 【core 】 CharSequenceUtil增加join重载(issue#I3TFJ5@Gitee)
|
||||||
* 【http 】 HttpRequest增加form方法重载(pr#337@Gitee)
|
* 【http 】 HttpRequest增加form方法重载(pr#337@Gitee)
|
||||||
* 【http 】 ImgUtil增加getMainColor方法(pr#338@Gitee)
|
* 【http 】 ImgUtil增加getMainColor方法(pr#338@Gitee)
|
||||||
|
* 【core 】 改进TreeUtil.buid算法性能(pr#1594@Github)
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复FileUtil.normalize去掉末尾空格问题(issue#1603@Github)
|
* 【core 】 修复FileUtil.normalize去掉末尾空格问题(issue#1603@Github)
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
package cn.hutool.core.lang.tree;
|
package cn.hutool.core.lang.tree;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
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.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.LinkedHashMap;
|
||||||
import java.util.List;
|
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());
|
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);
|
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 !");
|
Assert.notEmpty(key, "Key must be not empty !");
|
||||||
this.put(key, value);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,11 +3,13 @@ package cn.hutool.core.lang.tree;
|
|||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.tree.parser.DefaultNodeParser;
|
import cn.hutool.core.lang.tree.parser.DefaultNodeParser;
|
||||||
import cn.hutool.core.lang.tree.parser.NodeParser;
|
import cn.hutool.core.lang.tree.parser.NodeParser;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 树工具类
|
* 树工具类
|
||||||
@ -58,65 +60,49 @@ public class TreeUtil {
|
|||||||
* @param <T> 转换的实体 为数据源里的对象类型
|
* @param <T> 转换的实体 为数据源里的对象类型
|
||||||
* @param <E> ID类型
|
* @param <E> ID类型
|
||||||
* @param list 源数据集合
|
* @param list 源数据集合
|
||||||
* @param parentId 最顶层父id值 一般为 0 之类
|
* @param rootId 最顶层父id值 一般为 0 之类
|
||||||
* @param treeNodeConfig 配置
|
* @param treeNodeConfig 配置
|
||||||
* @param nodeParser 转换器
|
* @param nodeParser 转换器
|
||||||
* @return List
|
* @return List
|
||||||
*/
|
*/
|
||||||
public static <T, E> List<Tree<E>> build(List<T> list, E parentId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
|
public static <T, E> List<Tree<E>> build(List<T> list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
|
||||||
final List<Tree<E>> treeList = CollUtil.newArrayList();
|
final Map<E, Tree<E>> map = new LinkedHashMap<>(list.size(), 1);
|
||||||
Tree<E> tree;
|
Tree<E> node;
|
||||||
for (T obj : list) {
|
for (T t : list) {
|
||||||
tree = new Tree<>(treeNodeConfig);
|
node = new Tree<>(treeNodeConfig);
|
||||||
nodeParser.parse(obj, tree);
|
nodeParser.parse(t, node);
|
||||||
treeList.add(tree);
|
map.put(node.getId(), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Tree<E>> finalTreeList = CollUtil.newArrayList();
|
return build(map, rootId);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归处理
|
* 树构建,按照权重排序
|
||||||
*
|
*
|
||||||
* @param treeNodes 数据集合
|
* @param <E> ID类型
|
||||||
* @param parentNode 当前节点
|
* @param map 源数据Map
|
||||||
* @param deep 已递归深度
|
* @param rootId 最顶层父id值 一般为 0 之类
|
||||||
* @param maxDeep 最大递归深度 可能为null即不限制
|
* @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)) {
|
final Tree<E> parentNode = map.get(parentId);
|
||||||
return;
|
if(null != parentNode){
|
||||||
}
|
parentNode.addChildren(node);
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return rootTreeList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +124,7 @@ public class TreeUtil {
|
|||||||
if(null == children) {
|
if(null == children) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找子节点
|
// 查找子节点
|
||||||
Tree<T> childNode;
|
Tree<T> childNode;
|
||||||
for (Tree<T> child : children) {
|
for (Tree<T> child : children) {
|
||||||
@ -182,5 +168,4 @@ public class TreeUtil {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ public class TreeTest {
|
|||||||
static {
|
static {
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
nodeList.add(new TreeNode<>("1", "0", "系统管理", 5));
|
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<>("111", "11", "用户添加", 0));
|
||||||
|
nodeList.add(new TreeNode<>("11", "1", "用户管理", 222222));
|
||||||
|
|
||||||
nodeList.add(new TreeNode<>("2", "0", "店铺管理", 1));
|
nodeList.add(new TreeNode<>("2", "0", "店铺管理", 1));
|
||||||
nodeList.add(new TreeNode<>("21", "2", "商品管理", 44));
|
nodeList.add(new TreeNode<>("21", "2", "商品管理", 44));
|
||||||
@ -33,6 +33,7 @@ public class TreeTest {
|
|||||||
for (Tree<String> tree : treeList) {
|
for (Tree<String> tree : treeList) {
|
||||||
Assert.assertNotNull(tree);
|
Assert.assertNotNull(tree);
|
||||||
Assert.assertEquals("0", tree.getParentId());
|
Assert.assertEquals("0", tree.getParentId());
|
||||||
|
// Console.log(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试通过子节点查找父节点
|
// 测试通过子节点查找父节点
|
||||||
|
Loading…
x
Reference in New Issue
Block a user