Java中如何將list轉(zhuǎn)為樹(shù)形結(jié)構(gòu)
在系統(tǒng)開(kāi)發(fā)過(guò)程中,可能會(huì)碰到一些需求,需要構(gòu)建樹(shù)形結(jié)構(gòu),數(shù)據(jù)庫(kù)一般就使用父id來(lái)表示,比如構(gòu)建菜單、構(gòu)建地區(qū)級(jí)聯(lián)、構(gòu)建部門(mén)層級(jí)結(jié)構(gòu)等等。雖然可以通過(guò)數(shù)據(jù)庫(kù)SQL查詢,但我們一般都是通過(guò)SQL一次性查詢出所有數(shù)據(jù),在程序中處理成樹(shù)形結(jié)構(gòu)。本文講述如何將一個(gè)List<T> 處理成想要的樹(shù)形結(jié)構(gòu)。
1、自己測(cè)試過(guò)沒(méi)問(wèn)題的:
/** * 對(duì)象List轉(zhuǎn)為T(mén)ree樹(shù)形結(jié)構(gòu) * * @param entityList 傳進(jìn)來(lái)的泛型List * @param primaryFieldName 主鍵名稱(chēng) * @param parentFieldName 父級(jí)字段名稱(chēng) * @return */ public final List<Map<String, Object>> listToTree(List<Map<String, Object>> entityList, String primaryFieldName, String parentFieldName) { //返回的map Tree樹(shù)形結(jié)構(gòu) List<Map<String, Object>> treeMap = new ArrayList<>(); //將傳進(jìn)的參數(shù)entityList轉(zhuǎn)為MapList List<Map<String, Object>> listMap = JSON.parseObject(JSON.toJSONString(entityList), List.class); //聲明一個(gè)map用來(lái)存listMap中的對(duì)象,key為對(duì)象id,value為對(duì)象本身 Map<String, Map<String, Object>> entityMap = new Hashtable<>(); //循環(huán)listMap把map對(duì)象put到entityMap中去 listMap.forEach(map -> entityMap.put(map.get(primaryFieldName).toString(), map)); //循環(huán)listMap進(jìn)行Tree樹(shù)形結(jié)構(gòu)組裝 listMap.forEach(map -> { //獲取map的pid Object pid = map.get(parentFieldName); //判斷pid是否為空或者為0,為空說(shuō)明是最頂級(jí),直接add到返回的treeMap中去 if (pid == null || StringUtils.equals(pid.toString(), "0")) { treeMap.add(map); } else { //如果pid不為空也不為0,是子集 // 根據(jù)當(dāng)前map的pid獲取上級(jí) parentMap Map<String, Object> parentMap = entityMap.get(pid.toString()); if (parentMap == null) { //如果parentMap為空,則說(shuō)明當(dāng)前map沒(méi)有父級(jí),當(dāng)前map就是頂級(jí) treeMap.add(map); } else { //如果parentMap不為空,則當(dāng)前map為parentMap的子級(jí) //取出parentMap的所有子級(jí)的List集合 List<Map<String, Object>> children = (List<Map<String, Object>>) parentMap.get("children"); if (children == null) { //判斷子級(jí)集合是否為空,為空則新創(chuàng)建List children = new ArrayList<>(); parentMap.put("children", children); } //把當(dāng)前map對(duì)象add到parentMap的子級(jí)List中去 children.add(map); /** * 因?yàn)閜arentMap是從entityMap中g(shù)et出來(lái)的, * 而entityMap中的value又是來(lái)自于listMap對(duì)象, * 所以parentMap和entityMap中的value的地址都是指向listMap中的對(duì)象, * 所以parentMap的children和entityMap中的value的children改變時(shí),都會(huì)改變listMap中的對(duì)象, * 這里涉及到了地址、指針,就不多說(shuō)了。 */ } } }); return treeMap; }
2、for 方法轉(zhuǎn)樹(shù)形
public class TreeTest { public static void main(String[] args) { List<Tree> node = forMethod(treeList); System.out.println(node); } ? /** * 雙重for循環(huán)方法轉(zhuǎn)換成樹(shù)形結(jié)構(gòu) * @param treeList * @return */ public static List<Tree> forMethod(List<Tree> treeList) { List<Tree> rootTree = new ArrayList<>(); for (Tree tree : treeList) { // 第一步 篩選出最頂級(jí)的父節(jié)點(diǎn) if (0 == tree.getParentId()) { rootTree.add(tree); } // 第二步 篩選出該父節(jié)點(diǎn)下的所有子節(jié)點(diǎn)列表 for (Tree node : treeList) { if (node.getParentId().equals(tree.getId())) { if (CollectionUtils.isEmpty(tree.getChildren())) { tree.setChildren(new ArrayList<>()); } tree.getChildren().add(node); } } } return rootTree; } }
3、遞歸方法轉(zhuǎn)樹(shù)形
public class TreeTest { public static void main(String[] args) { List<Tree> node = recursionMethod(treeList); System.out.println(node); } /** * 遞歸方法轉(zhuǎn)換成樹(shù)形結(jié)構(gòu) * @param treeList * @return */ public static List<Tree> recursionMethod(List<Tree> treeList) { List<Tree> trees = new ArrayList<>(); for (Tree tree : treeList) { // 找出父節(jié)點(diǎn) if (0 == tree.getParentId()) { // 調(diào)用遞歸方法填充子節(jié)點(diǎn)列表 trees.add(findChildren(tree, treeList)); } } return trees; } ? /** * 遞歸方法 * @param tree 父節(jié)點(diǎn)對(duì)象 * @param treeList 所有的List * @return */ public static Tree findChildren(Tree tree, List<Tree> treeList) { for (Tree node : treeList) { if (tree.getId().equals(node.getParentId())) { if (tree.getChildren() == null) { tree.setChildren(new ArrayList<>()); } // 遞歸 調(diào)用自身 tree.getChildren().add(findChildren(node, treeList)); } } return tree; } }
4、stream方法轉(zhuǎn)樹(shù)形
public class TreeTest { public static void main(String[] args) { List<Tree> node = recursionMethod(treeList); System.out.println(node); } /** * stream方法轉(zhuǎn)換成樹(shù)形結(jié)構(gòu) * @param treeList * @return */ public static List<Tree> streamMethod(List<Tree> treeList) { List<Tree> list = treeList.stream() // 篩選出父節(jié)點(diǎn) .filter(t -> t.getParentId() == 0) // 設(shè)置父節(jié)點(diǎn)的子節(jié)點(diǎn)列表 .map(item -> {item.setChildren(streamGetChildren(item, treeList)); return item;}) .collect(Collectors.toList()); return list; } ? /** * stream 方式遞歸查找子節(jié)點(diǎn)列表 * @return */ public static List<Tree> streamGetChildren(Tree tree, List<Tree> treeList) { List<Tree> list = treeList.stream() .filter(t -> t.getParentId().equals(tree.getId())) .map(item -> {item.setChildren(streamGetChildren(item, treeList)); return item;}) .collect(Collectors.toList()); return list; } }
5、stream 轉(zhuǎn)樹(shù)形優(yōu)化
// 第一種優(yōu)化,我們合并上述兩個(gè)方法的相同部分 public class TreeTest { public static void main(String[] args) { List<Tree> node = streamMethod(0, treeList); System.out.println(node); } ? /** * stream 方法轉(zhuǎn)換樹(shù)形結(jié)構(gòu)方法的優(yōu)化 * @param parentId * @param treeList * @return */ public static List<Tree> streamMethod(Integer parentId, List<Tree> treeList) { List<Tree> list = treeList.stream() // 篩選父節(jié)點(diǎn) .filter(t -> t.getParentId().equals(parentId)) // 遞歸設(shè)置子節(jié)點(diǎn) .map(item -> { item.setChildren(streamMethod(item.getId(), treeList)); return item; }) .collect(Collectors.toList()); return list; } } // 第二種優(yōu)化,只是寫(xiě)法的不同,核心思路不變 public class TreeTest { public static void main(String[] args) { List<Tree> node = streamMethod(0, treeList); System.out.println(node); } /** * stream 方法轉(zhuǎn)換樹(shù)形結(jié)構(gòu)方法的優(yōu)化 * @param parentId * @param treeList * @return */ public static List<Tree> streamMethod(Integer parentId, List<Tree> treeList) { List<Tree> list = new ArrayList<>(); Optional.ofNullable(treeList).orElse(new ArrayList<>()) .stream() // 第一次篩選出主父節(jié)點(diǎn)列表進(jìn)入循環(huán),循環(huán)里面 進(jìn)入遞歸 篩選出遞歸傳遞的從父節(jié)點(diǎn)列表 .filter(root -> root.getParentId().equals(parentId)) // 遞歸,最末的父節(jié)點(diǎn)從整個(gè)列表篩選出它的子節(jié)點(diǎn)列表依次組裝 .forEach(tree -> { List<Tree> children = streamMethod(tree.getId(), treeList); tree.setChildren(children); list.add(tree); }); return list; } }
經(jīng)過(guò)上面的代碼,我們已經(jīng)成功的將 List<T> 轉(zhuǎn)換成了我們想要的樹(shù)形結(jié)構(gòu)了,但是此時(shí)有需求,要求我們文件夾名稱(chēng)后面附帶文件夾包含的文件數(shù)量,
數(shù)據(jù)庫(kù)或者構(gòu)造的集合數(shù)據(jù)中的文件數(shù)量只統(tǒng)計(jì)了文件夾本身下面的文件數(shù)量,并沒(méi)有加上子文件夾里面的所有文件夾數(shù)量。我們需要將子文件夾的 count 屬性逐
級(jí)累加到父節(jié)點(diǎn)的 count 屬性中更新它。
public class TreeTest { public static void main(String[] args) { // treeList 是已經(jīng)處理好的 樹(shù)形結(jié)構(gòu) 集合 countHandler(treeList); System.out.println(treeList); } ? /** * 遞歸將子節(jié)點(diǎn)屬性值累加給父節(jié)點(diǎn) * @param treeList * @return */ private int countHandler(List<Tree> treeList) { int count = 0; if(CollectionUtil.isEmpty(treeList)){ return count; } for (Tree tree : treeList) { count += tree.getCount(); if (CollectionUtil.isEmpty(tree.getChildren())) { continue; } count += countHandler(tree.getChildren()); tree.setCount(count); if (tree.getParentId() == null || tree.getParentId() == 0) { count = 0; } } return count; } }
到此這篇關(guān)于Java中將list轉(zhuǎn)為樹(shù)形結(jié)構(gòu)的文章就介紹到這了,更多相關(guān)java list轉(zhuǎn)樹(shù)形結(jié)構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java如何使用遞歸查詢多級(jí)樹(shù)形結(jié)構(gòu)數(shù)據(jù)(多級(jí)菜單)
- Java使用 Stream 流和 Lambda 組裝復(fù)雜父子樹(shù)形結(jié)構(gòu)
- java+vue3+el-tree實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)操作代碼
- java返回前端樹(shù)形結(jié)構(gòu)數(shù)據(jù)的2種實(shí)現(xiàn)方式
- 詳解如何使用Java流API構(gòu)建樹(shù)形結(jié)構(gòu)數(shù)據(jù)
- java如何讀取文件目錄返回樹(shù)形結(jié)構(gòu)
- Java樹(shù)形結(jié)構(gòu)遞歸查詢方式
相關(guān)文章
Java 詳解包裝類(lèi)Integer與int有哪些共通和不同
這篇文章主要介紹的是 Java中int和Integer的區(qū)別,Java 是一種強(qiáng)數(shù)據(jù)類(lèi)型的語(yǔ)言,因此所有的屬性必須有一個(gè)數(shù)據(jù)類(lèi)型,下面文章基于Java詳細(xì)int和Integer有何區(qū)別,需要的朋友可以參考一下2022-04-04Java字符串轉(zhuǎn)時(shí)間簡(jiǎn)單示例代碼
這篇文章主要給大家介紹了關(guān)于Java字符串轉(zhuǎn)時(shí)間的相關(guān)資料,在Java中字符和字符串常常需要相互轉(zhuǎn)化,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08java.lang.NullPointerException 如何處理空指針異常的實(shí)現(xiàn)
這篇文章主要介紹了java.lang.NullPointerException 如何處理空指針異常的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Springboot實(shí)現(xiàn)ModbusTCP通信的示例詳解
ModbusTCP協(xié)議是Modbus由MODICON公司于1979年開(kāi)發(fā),是一種工業(yè)現(xiàn)場(chǎng)總線協(xié)議標(biāo)準(zhǔn),本文主要介紹了Springboot實(shí)現(xiàn)ModbusTCP通信的相關(guān)知識(shí),需要的可以參考下2023-12-12Java實(shí)現(xiàn)FTP批量大文件上傳下載篇1
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)FTP批量大文件上傳下載的基礎(chǔ)篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08logback的AsyncAppender高效日志處理方式源碼解析
這篇文章主要為大家介紹了logback的AsyncAppender高效日志處理方式源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Java漢字轉(zhuǎn)成漢語(yǔ)拼音工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了Java漢字轉(zhuǎn)成漢語(yǔ)拼音工具類(lèi),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05