java之TreeUtils生成一切對(duì)象樹形結(jié)構(gòu)案例
項(xiàng)目中經(jīng)常會(huì)遇到各種需要以樹形結(jié)構(gòu)展示的功能,比較常見的,如菜單樹,分類樹,部門樹等等,如果為每種類型都遍歷遞歸生成樹形結(jié)構(gòu)返回給前端,顯得有些冗余且麻煩,并且其實(shí)邏輯都是一致的,只是遍歷的對(duì)象不同而已,故其實(shí)可以通過面向接口思維,來實(shí)現(xiàn)這種通用工具類的實(shí)現(xiàn)。
TreeNode用來表示每個(gè)樹節(jié)點(diǎn)的抽象,即需要生成樹的對(duì)象需要實(shí)現(xiàn)此接口。
/** * 樹節(jié)點(diǎn)父類,所有需要使用{@linkplain TreeUtils}工具類形成樹形結(jié)構(gòu)等操作的節(jié)點(diǎn)都需要實(shí)現(xiàn)該接口 * * @param <T> 節(jié)點(diǎn)id類型 */ public interface TreeNode<T> { /** * 獲取節(jié)點(diǎn)id * * @return 樹節(jié)點(diǎn)id */ T id(); /** * 獲取該節(jié)點(diǎn)的父節(jié)點(diǎn)id * * @return 父節(jié)點(diǎn)id */ T parentId(); /** * 是否是根節(jié)點(diǎn) * * @return true:根節(jié)點(diǎn) */ boolean root(); /** * 設(shè)置節(jié)點(diǎn)的子節(jié)點(diǎn)列表 * * @param children 子節(jié)點(diǎn) */ void setChildren(List<? extends TreeNode<T>> children); /** * 獲取所有子節(jié)點(diǎn) * * @return 子節(jié)點(diǎn)列表 */ List<? extends TreeNode<T>> getChildren(); }
TreeUtils用來生成樹形結(jié)構(gòu),以及獲取所有葉子節(jié)點(diǎn)等操作
/** * 樹形結(jié)構(gòu)工具類 * * @author meilin.huang * @version 1.0 * @date 2019-08-24 1:57 下午 */ public class TreeUtils { /** * 根據(jù)所有樹節(jié)點(diǎn)列表,生成含有所有樹形結(jié)構(gòu)的列表 * * @param nodes 樹形節(jié)點(diǎn)列表 * @param <T> 節(jié)點(diǎn)類型 * @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é)點(diǎn)列表中刪除該節(jié)點(diǎn),以免后續(xù)重復(fù)遍歷該節(jié)點(diǎn) ite.remove(); } } roots.forEach(r -> { setChildren(r, nodes); }); return roots; } /** * 從所有節(jié)點(diǎn)列表中查找并設(shè)置parent的所有子節(jié)點(diǎn) * * @param parent 父節(jié)點(diǎn) * @param nodes 所有樹節(jié)點(diǎn)列表 */ @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é)點(diǎn)列表中刪除該節(jié)點(diǎn),以免后續(xù)重復(fù)遍歷該節(jié)點(diǎn) ite.remove(); } } // 如果孩子為空,則直接返回,否則繼續(xù)遞歸設(shè)置孩子的孩子 if (children.isEmpty()) { return; } parent.setChildren(children); children.forEach(m -> { // 遞歸設(shè)置子節(jié)點(diǎn) setChildren(m, nodes); }); } /** * 獲取指定樹節(jié)點(diǎn)下的所有葉子節(jié)點(diǎn) * * @param parent 父節(jié)點(diǎn) * @param <T> 實(shí)際節(jié)點(diǎn)類型 * @return 葉子節(jié)點(diǎn) */ public static <T extends TreeNode<?>> List<T> getLeafs(T parent) { List<T> leafs = new ArrayList<>(); fillLeaf(parent, leafs); return leafs; } /** * 將parent的所有葉子節(jié)點(diǎn)填充至leafs列表中 * * @param parent 父節(jié)點(diǎn) * @param leafs 葉子節(jié)點(diǎn)列表 * @param <T> 實(shí)際節(jié)點(diǎn)類型 */ @SuppressWarnings("all") public static <T extends TreeNode> void fillLeaf(T parent, List<T> leafs) { List<T> children = parent.getChildren(); // 如果節(jié)點(diǎn)沒有子節(jié)點(diǎn)則說明為葉子節(jié)點(diǎn) if (CollectionUtils.isEmpty(children)) { leafs.add(parent); return; } // 遞歸調(diào)用子節(jié)點(diǎn),查找葉子節(jié)點(diǎn) for (T child : children) { fillLeaf(child, leafs); } } }
具體使用方式之聲明樹節(jié)點(diǎn)對(duì)象
@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)用
/** * 獲取賬號(hào)的資源樹 */ public List<ResourceListVO> listByAccountId(Long accountId) { return TreeUtils.generateTrees(BeanUtils.copyProperties(mapper.selectByAccountId(userId), ResourceListVO.class)); }
通過使用TreeUtils工具可以統(tǒng)一方便地生成一切對(duì)象的樹形結(jié)構(gòu)以及其他一些對(duì)樹的操作,避免對(duì)每個(gè)對(duì)象都用特定代碼生成。使用起來就是幾個(gè)字簡(jiǎn)潔方便爽歪歪biu特否。
補(bǔ)充知識(shí):TreeUtil 數(shù)據(jù)庫(kù)菜單生成無(wú)限級(jí)樹形結(jié)構(gòu)
1、項(xiàng)目需求:
從數(shù)據(jù)庫(kù)從加載所有的菜單出來,菜單中有
id,parentId,name字段
希望能有一個(gè)工具幫我進(jìn)行樹形結(jié)構(gòu)重組;
實(shí)例類:
package com.lming.chcservice.util; import lombok.Data; import java.util.List; @Data public class TreeNode { /** * 節(jié)點(diǎn)id */ private String id; /** * 父節(jié)點(diǎn) 默認(rèn)0為根節(jié)點(diǎn) */ private String parentId; /** * 節(jié)點(diǎn)名稱 */ private String name; /** * 是否有子節(jié)點(diǎn) */ private boolean hasChild; public TreeNode(String id, String parentId, String name) { this.id = id; this.parentId = parentId; this.name = name; } }
工具類:
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對(duì)象轉(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","首頁(yè)"); TreeNode treeNode2 = new TreeNode("2","0","訂單"); TreeNode treeNode3 = new TreeNode("3","1","預(yù)約"); TreeNode treeNode4 = new TreeNode("4","2","捐獻(xiàn)"); TreeNode treeNode5 = new TreeNode("5","4","我的訂單"); TreeNode treeNode6 = new TreeNode("6","5","個(gè)人中心"); TreeNode treeNode7 = new TreeNode("7","6","個(gè)人中心2"); TreeNode treeNode8 = new TreeNode("8","99","個(gè)人中心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))); } }
測(cè)試結(jié)果:
[ { "id": "1", "name": "首頁(yè)", "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": "捐獻(xiàn)", "parentId": "2", "hasChild": true, "childrens": [ { "id": "5", "name": "我的訂單", "parentId": "4", "hasChild": true, "childrens": [ { "id": "6", "name": "個(gè)人中心", "parentId": "5", "hasChild": true, "childrens": [ { "id": "7", "name": "個(gè)人中心2", "parentId": "6", "hasChildren": false, "childrens": [] } ] } ] } ] } ] } ]
實(shí)力類不一致怎么辦? 自己寫一個(gè)實(shí)體轉(zhuǎn)換類,將類的對(duì)象屬性轉(zhuǎn)換成上面的實(shí)體類,然后在調(diào)用,當(dāng)然最快的方式直接修改實(shí)體類即可用。
以上這篇java之TreeUtils生成一切對(duì)象樹形結(jié)構(gòu)案例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)百度坐標(biāo)的摩卡托坐標(biāo)與火星坐標(biāo)轉(zhuǎn)換的示例
這篇文章主要介紹了java實(shí)現(xiàn)百度坐標(biāo)的摩卡托坐標(biāo)與火星坐標(biāo)轉(zhuǎn)換的示例,需要的朋友可以參考下2014-03-03使用maven-archetype-plugin現(xiàn)有項(xiàng)目生成腳手架的方法
這篇文章主要介紹了使用maven-archetype-plugin現(xiàn)有項(xiàng)目生成腳手架的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11淺談@RequestParam(required = true)的誤區(qū)
這篇文章主要介紹了@RequestParam(required = true)的誤區(qū),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11手把手帶你實(shí)現(xiàn)一個(gè)萌芽版的Spring容器
大家好,我是老三,Spring是我們最常用的開源框架,經(jīng)過多年發(fā)展,Spring已經(jīng)發(fā)展成枝繁葉茂的大樹,讓我們難以窺其全貌,這節(jié),我們回歸Spring的本質(zhì),五分鐘手?jǐn)]一個(gè)Spring容器,揭開Spring神秘的面紗2022-03-03Mybatis-plus多數(shù)據(jù)源配置的兩種方式總結(jié)
這篇文章主要為大家詳細(xì)介紹了Mybatis-plus中多數(shù)據(jù)源配置的兩種方式,文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起了解一下2022-10-10mybatis-plus開啟sql打印的三種方式總結(jié)
這篇文章主要給大家介紹了mybatisplus開啟sql打印的三種方式,文章通過代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考價(jià)值,需要的朋友可以參考下2023-11-11Java中的DelayQueue實(shí)現(xiàn)原理及應(yīng)用場(chǎng)景詳解
這篇文章主要介紹了Java中的DelayQueue實(shí)現(xiàn)原理及應(yīng)用場(chǎng)景詳解,DelayQueue是一個(gè)沒有邊界BlockingQueue實(shí)現(xiàn),加入其中的元素必需實(shí)現(xiàn)Delayed接口,當(dāng)生產(chǎn)者線程調(diào)用put之類的方法加入元素時(shí),會(huì)觸發(fā)Delayed接口中的compareTo方法進(jìn)行排序,需要的朋友可以參考下2023-12-12SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況
這篇文章主要介紹了SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05