项目中经常会遇到各种需要以树形结构展示的功能,比较常见的,如菜单树,分类树,部门树等等,如果为每种类型都遍历递归生成树形结构返回给前端,显得有些冗余且麻烦,并且其实逻辑都是一致的,只是遍历的对象不同而已,故其实可以通过面向接口思维,来实现这种通用工具类的实现。
TreeNode用来表示每个树节点的抽象,即需要生成树的对象需要实现此接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/**
* 树节点父类,所有需要使用{@linkplain TreeUtils}工具类形成树形结构等操作的节点都需要实现该接口
*
* @param <T> 节点id类型
*/
public interface TreeNode<T> {
/**
* 获取节点id
*
* @return 树节点id
*/
T id();
/**
* 获取该节点的父节点id
*
* @return 父节点id
*/
T parentId();
/**
* 是否是根节点
*
* @return true:根节点
*/
boolean root();
/**
* 设置节点的子节点列表
*
* @param children 子节点
*/
void setChildren(List<? extends TreeNode<T>> children);
/**
* 获取所有子节点
*
* @return 子节点列表
*/
List<? extends TreeNode<T>> getChildren();
}
|
TreeUtils用来生成树形结构,以及获取所有叶子节点等操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
/**
* 树形结构工具类
*
* @author meilin.huang
* @version 1.0
* @date 2019-08-24 1:57 下午
*/
public class TreeUtils {
/**
* 根据所有树节点列表,生成含有所有树形结构的列表
*
* @param nodes 树形节点列表
* @param <T> 节点类型
* @return 树形结构列表
*/
public static <T extends TreeNode<?>> List<T> generateTrees(List<T> nodes) {
List<T> roots = new ArrayList<>();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (node.root()) {
roots.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
roots.forEach(r -> {
setChildren(r, nodes);
});
return roots;
}
/**
* 从所有节点列表中查找并设置parent的所有子节点
*
* @param parent 父节点
* @param nodes 所有树节点列表
*/
@SuppressWarnings ( "all" )
public static <T extends TreeNode> void setChildren(T parent, List<T> nodes) {
List<T> children = new ArrayList<>();
Object parentId = parent.id();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (Objects.equals(node.parentId(), parentId)) {
children.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
// 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
if (children.isEmpty()) {
return ;
}
parent.setChildren(children);
children.forEach(m -> {
// 递归设置子节点
setChildren(m, nodes);
});
}
/**
* 获取指定树节点下的所有叶子节点
*
* @param parent 父节点
* @param <T> 实际节点类型
* @return 叶子节点
*/
public static <T extends TreeNode<?>> List<T> getLeafs(T parent) {
List<T> leafs = new ArrayList<>();
fillLeaf(parent, leafs);
return leafs;
}
/**
* 将parent的所有叶子节点填充至leafs列表中
*
* @param parent 父节点
* @param leafs 叶子节点列表
* @param <T> 实际节点类型
*/
@SuppressWarnings ( "all" )
public static <T extends TreeNode> void fillLeaf(T parent, List<T> leafs) {
List<T> children = parent.getChildren();
// 如果节点没有子节点则说明为叶子节点
if (CollectionUtils.isEmpty(children)) {
leafs.add(parent);
return ;
}
// 递归调用子节点,查找叶子节点
for (T child : children) {
fillLeaf(child, leafs);
}
}
}
|
具体使用方式之声明树节点对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
@Getter
@Setter
public class ResourceListVO implements TreeNode<Long> {
private Long id;
private Long pid;
private Integer type;
private String name;
private String icon;
private String code;
private Integer status;
private List<ResourceListVO> children;
@Override
public Long id() {
return this .id;
}
@Override
public Long parentId() {
return this .pid;
}
@Override
public boolean root() {
return Objects.equals( this .pid, 0L);
}
@Override
public void setChildren(List children) {
this .children = children;
}
}
|
具体使用方式之调用
1
2
3
4
5
6
|
/**
* 获取账号的资源树
*/
public List<ResourceListVO> listByAccountId(Long accountId) {
return TreeUtils.generateTrees(BeanUtils.copyProperties(mapper.selectByAccountId(userId), ResourceListVO. class ));
}
|
通过使用TreeUtils工具可以统一方便地生成一切对象的树形结构以及其他一些对树的操作,避免对每个对象都用特定代码生成。使用起来就是几个字简洁方便爽歪歪biu特否。
补充知识:TreeUtil 数据库菜单生成无限级树形结构
1、项目需求:
从数据库从加载所有的菜单出来,菜单中有
id,parentId,name字段
希望能有一个工具帮我进行树形结构重组;
实例类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package com.lming.chcservice.util;
import lombok.Data;
import java.util.List;
@Data
public class TreeNode {
/**
* 节点id
*/
private String id;
/**
* 父节点 默认0为根节点
*/
private String parentId;
/**
* 节点名称
*/
private String name;
/**
* 是否有子节点
*/
private boolean hasChild;
public TreeNode(String id, String parentId, String name) {
this .id = id;
this .parentId = parentId;
this .name = name;
}
}
|
工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
package com.lming.chcservice.util;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 树形结构工具类
*
* 将一组list对象转成树形结构
* 该list需符合设定的字段类型
*
*/
public class TreeUtil {
public static Map<String,Object> mapArray = new LinkedHashMap<String, Object>();
public List<TreeNode> menuCommon;
public List<Object> list = new ArrayList<Object>();
public List<Object> treeMenu(List<TreeNode> menu){
this .menuCommon = menu;
for (TreeNode treeNode : menu) {
Map<String,Object> mapArr = new LinkedHashMap<String, Object>();
if (treeNode.getParentId().equals( "0" )){
setTreeMap(mapArr,treeNode);
list.add(mapArr);
}
}
return list;
}
public List<?> menuChild(String id){
List<Object> lists = new ArrayList<Object>();
for (TreeNode a:menuCommon){
Map<String,Object> childArray = new LinkedHashMap<String, Object>();
if (a.getParentId() .equals(id)){
setTreeMap(childArray,a);
lists.add(childArray);
}
}
return lists;
}
private void setTreeMap(Map<String,Object> mapArr,TreeNode treeNode){
mapArr.put( "id" , treeNode.getId());
mapArr.put( "name" , treeNode.getName());
mapArr.put( "parentId" , treeNode.getParentId());
List<?> childrens = menuChild(treeNode.getId());
if (childrens.size()> 0 ){
mapArr.put( "hasChild" , true );
}
else {
mapArr.put( "hasChildren" , false );
}
mapArr.put( "childrens" , menuChild(treeNode.getId()));
}
public static void main(String[] args){
List<TreeNode> treeNodeList = new ArrayList<>();
TreeNode treeNode1 = new TreeNode( "1" , "0" , "首页" );
TreeNode treeNode2 = new TreeNode( "2" , "0" , "订单" );
TreeNode treeNode3 = new TreeNode( "3" , "1" , "预约" );
TreeNode treeNode4 = new TreeNode( "4" , "2" , "捐献" );
TreeNode treeNode5 = new TreeNode( "5" , "4" , "我的订单" );
TreeNode treeNode6 = new TreeNode( "6" , "5" , "个人中心" );
TreeNode treeNode7 = new TreeNode( "7" , "6" , "个人中心2" );
TreeNode treeNode8 = new TreeNode( "8" , "99" , "个人中心3" );
treeNodeList.add(treeNode1);
treeNodeList.add(treeNode6);
treeNodeList.add(treeNode5);
treeNodeList.add(treeNode3);
treeNodeList.add(treeNode4);
treeNodeList.add(treeNode2);
treeNodeList.add(treeNode7);
treeNodeList.add(treeNode8);
TreeUtil treeUtil = new TreeUtil();
System.out.print(JsonUtil.toJson(treeUtil.treeMenu(treeNodeList)));
}
}
|
测试结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
[
{
"id" : "1" ,
"name" : "首页" ,
"parentId" : "0" ,
"hasChild" : true ,
"childrens" : [
{
"id" : "3" ,
"name" : "预约" ,
"parentId" : "1" ,
"hasChildren" : false ,
"childrens" : []
}
]
},
{
"id" : "2" ,
"name" : "订单" ,
"parentId" : "0" ,
"hasChild" : true ,
"childrens" : [
{
"id" : "4" ,
"name" : "捐献" ,
"parentId" : "2" ,
"hasChild" : true ,
"childrens" : [
{
"id" : "5" ,
"name" : "我的订单" ,
"parentId" : "4" ,
"hasChild" : true ,
"childrens" : [
{
"id" : "6" ,
"name" : "个人中心" ,
"parentId" : "5" ,
"hasChild" : true ,
"childrens" : [
{
"id" : "7" ,
"name" : "个人中心2" ,
"parentId" : "6" ,
"hasChildren" : false ,
"childrens" : []
}
]
}
]
}
]
}
]
}
]
|
实力类不一致怎么办? 自己写一个实体转换类,将类的对象属性转换成上面的实体类,然后在调用,当然最快的方式直接修改实体类即可用。
以上这篇java之TreeUtils生成一切对象树形结构案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/mayfly_hml/article/details/105269111