項目中經(jīng)常會遇到各種需要以樹形結(jié)構(gòu)展示的功能,比較常見的,如菜單樹,分類樹,部門樹等等,如果為每種類型都遍歷遞歸生成樹形結(jié)構(gòu)返回給前端,顯得有些冗余且麻煩,并且其實邏輯都是一致的,只是遍歷的對象不同而已,故其實可以通過面向接口思維,來實現(xiàn)這種通用工具類的實現(xiàn)。
TreeNode用來表示每個樹節(jié)點的抽象,即需要生成樹的對象需要實現(xiàn)此接口。
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
|
/** * 樹節(jié)點父類,所有需要使用{@linkplain TreeUtils}工具類形成樹形結(jié)構(gòu)等操作的節(jié)點都需要實現(xiàn)該接口 * * @param <T> 節(jié)點id類型 */ public interface TreeNode<T> { /** * 獲取節(jié)點id * * @return 樹節(jié)點id */ T id(); /** * 獲取該節(jié)點的父節(jié)點id * * @return 父節(jié)點id */ T parentId(); /** * 是否是根節(jié)點 * * @return true:根節(jié)點 */ boolean root(); /** * 設(shè)置節(jié)點的子節(jié)點列表 * * @param children 子節(jié)點 */ void setChildren(List<? extends TreeNode<T>> children); /** * 獲取所有子節(jié)點 * * @return 子節(jié)點列表 */ List<? extends TreeNode<T>> getChildren(); } |
TreeUtils用來生成樹形結(jié)構(gòu),以及獲取所有葉子節(jié)點等操作
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
|
/** * 樹形結(jié)構(gòu)工具類 * * @author meilin.huang * @version 1.0 * @date 2019-08-24 1:57 下午 */ public class TreeUtils { /** * 根據(jù)所有樹節(jié)點列表,生成含有所有樹形結(jié)構(gòu)的列表 * * @param nodes 樹形節(jié)點列表 * @param <T> 節(jié)點類型 * @return 樹形結(jié)構(gòu)列表 */ 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); // 從所有節(jié)點列表中刪除該節(jié)點,以免后續(xù)重復(fù)遍歷該節(jié)點 ite.remove(); } } roots.forEach(r -> { setChildren(r, nodes); }); return roots; } /** * 從所有節(jié)點列表中查找并設(shè)置parent的所有子節(jié)點 * * @param parent 父節(jié)點 * @param nodes 所有樹節(jié)點列表 */ @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); // 從所有節(jié)點列表中刪除該節(jié)點,以免后續(xù)重復(fù)遍歷該節(jié)點 ite.remove(); } } // 如果孩子為空,則直接返回,否則繼續(xù)遞歸設(shè)置孩子的孩子 if (children.isEmpty()) { return ; } parent.setChildren(children); children.forEach(m -> { // 遞歸設(shè)置子節(jié)點 setChildren(m, nodes); }); } /** * 獲取指定樹節(jié)點下的所有葉子節(jié)點 * * @param parent 父節(jié)點 * @param <T> 實際節(jié)點類型 * @return 葉子節(jié)點 */ public static <T extends TreeNode<?>> List<T> getLeafs(T parent) { List<T> leafs = new ArrayList<>(); fillLeaf(parent, leafs); return leafs; } /** * 將parent的所有葉子節(jié)點填充至leafs列表中 * * @param parent 父節(jié)點 * @param leafs 葉子節(jié)點列表 * @param <T> 實際節(jié)點類型 */ @SuppressWarnings ( "all" ) public static <T extends TreeNode> void fillLeaf(T parent, List<T> leafs) { List<T> children = parent.getChildren(); // 如果節(jié)點沒有子節(jié)點則說明為葉子節(jié)點 if (CollectionUtils.isEmpty(children)) { leafs.add(parent); return ; } // 遞歸調(diào)用子節(jié)點,查找葉子節(jié)點 for (T child : children) { fillLeaf(child, leafs); } } } |
具體使用方式之聲明樹節(jié)點對象
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; } } |
具體使用方式之調(diào)用
1
2
3
4
5
6
|
/** * 獲取賬號的資源樹 */ public List<ResourceListVO> listByAccountId(Long accountId) { return TreeUtils.generateTrees(BeanUtils.copyProperties(mapper.selectByAccountId(userId), ResourceListVO. class )); } |
通過使用TreeUtils工具可以統(tǒng)一方便地生成一切對象的樹形結(jié)構(gòu)以及其他一些對樹的操作,避免對每個對象都用特定代碼生成。使用起來就是幾個字簡潔方便爽歪歪biu特否。
補充知識:TreeUtil 數(shù)據(jù)庫菜單生成無限級樹形結(jié)構(gòu)
1、項目需求:
從數(shù)據(jù)庫從加載所有的菜單出來,菜單中有
id,parentId,name字段
希望能有一個工具幫我進行樹形結(jié)構(gòu)重組;
實例類:
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 { /** * 節(jié)點id */ private String id; /** * 父節(jié)點 默認0為根節(jié)點 */ private String parentId; /** * 節(jié)點名稱 */ private String name; /** * 是否有子節(jié)點 */ 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; /** * 樹形結(jié)構(gòu)工具類 * * 將一組list對象轉(zhuǎn)成樹形結(jié)構(gòu) * 該list需符合設(shè)定的字段類型 * */ 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" , "預(yù)約" ); 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))); } } |
測試結(jié)果:
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" : "預(yù)約" , "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" : [] } ] } ] } ] } ] } ] |
實力類不一致怎么辦? 自己寫一個實體轉(zhuǎn)換類,將類的對象屬性轉(zhuǎn)換成上面的實體類,然后在調(diào)用,當然最快的方式直接修改實體類即可用。
以上這篇java之TreeUtils生成一切對象樹形結(jié)構(gòu)案例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/mayfly_hml/article/details/105269111