最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中實現(xiàn)樹形菜單的兩種方式

 更新時間:2023年09月26日 10:00:39   作者:我還是個孩子  
這篇文中,我一共會用兩種方式來實現(xiàn)目錄樹的數(shù)據(jù)結(jié)構,兩種寫法邏輯是一樣的,只是一種適合新手理解,一種看著簡單明了但是對于小白不是很好理解,在這里我會很詳細的講解每一步代碼,主要是方便新人看懂,彌補曾經(jīng)自己學習過程中的苦惱,需要的朋友可以參考下

一、什么是目錄結(jié)構?

就是在實際開發(fā)過程中,總會遇到菜單,或則是權限,這個時候就涉及到后端返回數(shù)據(jù)給前端的時候,不能一個集合把數(shù)據(jù)一股腦的全部扔給前端,總要把數(shù)據(jù)整理好,做成像書目錄一樣的結(jié)構返回給前端。就像以下圖示一樣

二、目錄樹結(jié)構實現(xiàn)寫法

1、準備階段

①創(chuàng)建數(shù)據(jù)表

PS:如果是練習可以不用創(chuàng)建數(shù)據(jù)庫,數(shù)據(jù)全部通過java代碼來創(chuàng)建也可以

CREATE TABLE permission_directory (
id int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
parent_id int(11) NOT NULL DEFAULT '0' COMMENT '父目錄ID',
menu_name varchar(255) NOT NULL COMMENT '菜單名稱',
menu_level int(11) NOT NULL COMMENT '菜單等級',
route varchar(255) NOT NULL COMMENT '路由',
PRIMARY KEY (id) COMMENT '主鍵',
UNIQUE KEY parent_id (parent_id,menu_name,menu_level,route) COMMENT '唯一索引,包含父目錄ID、菜單名稱、菜單等級和路由'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '存儲引擎為InnoDB,字符集為utf8';

②向表中插入數(shù)據(jù)

INSERT INTO permission_directory (parent_id, menu_name, menu_level, route) VALUES
(1, '首頁', 0, '/index'),
(2, '系統(tǒng)設置', 0, '/user/manage'),
(3, '操作手冊', 0, '/role/manage'),
(4, '菜單管理', 2, '/menu/manage'),
(5, '用戶管理', 2, '/system/setting'),
(6, '日志管理', 3, '/log/manage'),
(7, '定時任務', 3, '/task/schedule'),
(8, 'API接口文檔', 3, '/api/documentation'),
(9, '操作手冊', 8, '/operation/manual');

③創(chuàng)建菜單對象PermissionDirectory

PS:這里我用了@Data注解,就不用封裝屬性了,如果沒寫@Data注解就把每個屬性封裝以下,也就是get()和set()方法

@Data
public class PermissionDirectory {
    @MyAnnotation("主鍵id")
    private int id;
    @MyAnnotation("父目錄id")
    private int parentId;
    @MyAnnotation("菜單名稱")
    private String menuName;
    @MyAnnotation("菜單等級")
    private int menuLevel;
    @MyAnnotation("路由")
    private String route;
}

④創(chuàng)建存儲菜單對象PermissionDirectoryResVO

@Data
public class PermissionDirectoryResVO {
    @MyAnnotation("主鍵id")
    private Integer id;
    @MyAnnotation("父目錄id")
    private Integer parentId;
    @MyAnnotation("菜單名稱")
    private String menuName;
    @MyAnnotation("菜單等級")
    private Integer menuLevel;
    @MyAnnotation("路由")
    private String route;
    @MyAnnotation("用于存儲當前目錄下面的全部子集")
    private List<PermissionDirectoryResVO> authMenuList;
}

2、邏輯代碼實現(xiàn)

這里關于如何去連接數(shù)據(jù)庫啊等等一系列都省略了,關鍵就是目錄樹的邏輯講解

①第一種寫法

    public List<PermissionDirectoryResVO> searchMenu() {
        List<PermissionDirectoryResVO> directoryTree = new ArrayList<>();
        List<PermissionDirectory> menuList = permissionDirectoryMapper.getMenuList();
        if (CollectionUtil.isNotEmpty(menuList)){
            List<PermissionDirectoryResVO> pdr = menuList.stream().map(PermissionDirectory -> {
                PermissionDirectoryResVO permissionDirectoryResVO = new PermissionDirectoryResVO();
                BeanUtils.copyProperties(PermissionDirectory,permissionDirectoryResVO);
                return permissionDirectoryResVO;
            }).collect(Collectors.toList());
            pdr.forEach(e ->{
                List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr);
                e.setAuthMenuList(pdrList != null ? pdrList : null);
            });
            List<PermissionDirectoryResVO> parentNodes = pdr.stream().
                    filter(e -> e.getParentId().equals(0)).collect(Collectors.toList());
            directoryTree.addAll(parentNodes);
        }
        return directoryTree;
    }
    /**
     * 獲取全部子集
     * @param id
     * @param list
     * @return
     */
    public static List<PermissionDirectoryResVO> getChildrenList(Integer id, List<PermissionDirectoryResVO> list){
        return list.stream().filter(t-> t.getParentId().equals(id)).collect(Collectors.toList());
    }
}

第一種寫法代碼詳細解

第一步:創(chuàng)建存儲最終結(jié)果數(shù)據(jù)的集合容器
    List<PermissionDirectoryResVO> directoryTree = new ArrayList<>();
第二步:獲取需要整理成樹狀結(jié)構的所有數(shù)據(jù)
    List<PermissionDirectory> menuList = permissionDirectoryMapper.getMenuList();
	PS:這里我是通過查詢數(shù)據(jù)獲取的數(shù)據(jù),練習的話,可以new一些數(shù)據(jù)出來存入集合中就行了
第三步:判斷獲取的數(shù)據(jù)是否為空,如果為空的話就沒有去整理成樹結(jié)構的必要了,數(shù)據(jù)都沒有
    if (CollectionUtil.isNotEmpty(menuList)){ .... }
	PS:這里我用的是糊涂類提供的方法進行判斷,如果小白在寫的過程中發(fā)現(xiàn)報錯,找不到這個方法或則這個類就換一種寫法
第四步:將獲取的PermissionDirectory數(shù)據(jù)全部賦值給PermissionDirectoryResVO
     List<PermissionDirectoryResVO> pdr = menuList.stream().map(PermissionDirectory -> {
     	PermissionDirectoryResVO permissionDirectoryResVO = new PermissionDirectoryResVO();
     	BeanUtils.copyProperties(PermissionDirectory,permissionDirectoryResVO);
     	return permissionDirectoryResVO;
     }).collect(Collectors.toList());
	 具體解釋如下:
        menuList.stream():將menuList集合轉(zhuǎn)換為一個流(Stream)
        map(PermissionDirectory -> {...}):這個簡單理解就是循環(huán)menuList集合,然后遍歷集合中的每一個PermissionDirectory元素
        BeanUtils.copyProperties(PermissionDirectory,permissionDirectoryResVO):將PermissionDirectory對象的屬性值復制到permissionDirectoryResVO對象中。這樣,authMenuResVO對象就具有了與AuthMenu對象相同的屬性值。
        return permissionDirectoryResVO:將轉(zhuǎn)換后的permissionDirectoryResVO對象作為結(jié)果返回給調(diào)用者。
        collect(Collectors.toList()):將處理后的流中的元素收集到一個新的列表中,并返回該列表
        因此,這段代碼的作用是將原始列表menuList中的每個元素轉(zhuǎn)換為AuthMenuResVO類型的對象,并將轉(zhuǎn)換后的對象存儲在一個新的列表permissionDirectoryResVO中。
第五步:寫一個獲取子集的方法體
        public static List<PermissionDirectoryResVO> getChildrenList(Integer id, List<PermissionDirectoryResVO> list){
        	return list.stream().filter(t-> t.getParentId().equals(id)).collect(Collectors.toList());
    	}
       具體解釋如下:
           forEach(e -> {...}):是list對象的一個方法,用于遍歷該列表(或集合)中的每個元素,并對每個元素執(zhí)行一段操作。
           e -> {...}是一個Lambda表達式,表示對每個元素執(zhí)行的操作,相當于e就是PermissionDirectoryResVO元素對象
           因此,這段代碼就是通過傳遞一個主鍵id和一個PermissionDirectoryResVO集合對象參數(shù),然后遍歷循環(huán)PermissionDirectoryResVO對象集合,把每一個對象的父目錄id和傳遞過來的參數(shù)id進行對比,如果父目錄id等于參數(shù)id就把這個對象收集到新的集合中,最后作為參數(shù)返回。
第六步:遍歷全部數(shù)據(jù),利用遞歸思想,獲取全部的子集
      	  pdr.forEach(e ->{
          	List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr);
            e.setAuthMenuList(pdrList != null ? pdrList : null);
           });
		具體解釋如下:
            List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr);這一步通過調(diào)用第五步寫好的方法已經(jīng)獲取到了全部子集,就是說,如果所有數(shù)據(jù)一集目錄有三個,分別是1、2、3,那么當循環(huán)完的時候會有3個pdrList集合,每個集合中分別裝有1目錄下的數(shù)據(jù)、2目錄下的數(shù)據(jù)、3目錄下的數(shù)據(jù)。
            當每一次循環(huán)的時候,都會對pdr集合中的元素進行一次判斷,e.setAuthMenuList(pdrList != null ? pdrList : null);使用三目運算符,如果pdrList集合不為空就表示當前元素有子集,然把pdrList集合賦值給元素的authMenuList屬性,如果為空就表示沒有子集,賦值空就可以。
            當集合遍歷完畢,數(shù)據(jù)情況看圖①實例
第七步:獲取所有頂點數(shù)據(jù)
           List<PermissionDirectoryResVO> parentNodes = pdr.stream().
           		filter(e -> e.getParentId().equals(0)).collect(Collectors.toList());
           directoryTree.addAll(parentNodes);
		   具體解釋如下:
               判斷pdr集合中父目錄id為0的數(shù)據(jù),然后賦值給新的parentNodes,最后把這個集合存進directoryTree集合容器中

圖①

②第二種寫法

    public List<PermissionDirectoryResVO> searchMenu() {
        List<PermissionDirectoryResVO> directoryTree = new ArrayList<>();
        // 獲取全部數(shù)據(jù)
        List<PermissionDirectory> menuList = permissionDirectoryMapper.getMenuList();
        // 創(chuàng)建存儲PermissionDirectoryResVO對象的集合容器
        List<PermissionDirectoryResVO> pdr = new ArrayList<>();
        // 判斷集合中數(shù)據(jù)是否為空,不為空進行樹結(jié)構排列
        if (CollectionUtil.isNotEmpty(menuList)){
            // 遍歷循環(huán)集合menuList元素賦值給pdr集合中元素對象,這里就是第一種寫法的第四步
            for (PermissionDirectory permissionDirectory : menuList){
                PermissionDirectoryResVO permissionDirectoryResVO = new PermissionDirectoryResVO();
                permissionDirectoryResVO.setId(permissionDirectory.getId());
                permissionDirectoryResVO.setParentId(permissionDirectory.getParentId());
                permissionDirectoryResVO.setMenuName(permissionDirectory.getMenuName());
                permissionDirectoryResVO.setMenuLevel(permissionDirectory.getMenuLevel());
                permissionDirectoryResVO.setRoute(permissionDirectory.getRoute());
                pdr.add(permissionDirectoryResVO);
            }
        }
        // 遍歷全部數(shù)據(jù),利用遞歸思想,獲取全部的子集,第一種寫法的第六步
        for (PermissionDirectoryResVO e : pdr){
            List<PermissionDirectoryResVO> pdrList = getChildrenList(e.getId(),pdr);
            e.setAuthMenuList(pdrList != null ? pdrList : null);
        }
        // 獲取所有頂點數(shù)據(jù)
        for (PermissionDirectoryResVO e : pdr){
            if (e.getParentId().equals(0)){
                directoryTree.add(e);
            }
        }
        return directoryTree;
    }
    /**
     * 獲取全部子集
     * @param id
     * @param list
     * @return
     */
    public static List<PermissionDirectoryResVO> getChildrenList(Integer id, List<PermissionDirectoryResVO> list){
        List<PermissionDirectoryResVO> pdr = new ArrayList<>();
        // 這里就是第一種寫法的第五步
        for (PermissionDirectoryResVO per : list){
            if (per.getParentId().equals(id)){
                pdr.add(per);
            }
        }
        return pdr;
    }
}

最終結(jié)果

{
    "code": 200,
    "msg": "操作成功",
    "data": [
        {
            "id": 3,
            "parentId": 0,
            "menuName": "操作手冊",
            "menuLevel": 1,
            "route": "/role/manage",
            "authMenuList": [
                {
                    "id": 8,
                    "parentId": 3,
                    "menuName": "API接口文檔",
                    "menuLevel": 2,
                    "route": "/api/documentation",
                    "authMenuList": [
                        {
                            "id": 9,
                            "parentId": 8,
                            "menuName": "操作手冊",
                            "menuLevel": 3,
                            "route": "/operation/manual",
                            "authMenuList": []
                        }
                    ]
                },
                {
                    "id": 7,
                    "parentId": 3,
                    "menuName": "定時任務",
                    "menuLevel": 2,
                    "route": "/task/schedule",
                    "authMenuList": []
                },
                {
                    "id": 6,
                    "parentId": 3,
                    "menuName": "日志管理",
                    "menuLevel": 2,
                    "route": "/log/manage",
                    "authMenuList": []
                }
            ]
        },
        {
            "id": 2,
            "parentId": 0,
            "menuName": "系統(tǒng)設置",
            "menuLevel": 1,
            "route": "/user/manage",
            "authMenuList": [
                {
                    "id": 5,
                    "parentId": 2,
                    "menuName": "用戶管理",
                    "menuLevel": 2,
                    "route": "/system/setting",
                    "authMenuList": []
                },
                {
                    "id": 4,
                    "parentId": 2,
                    "menuName": "菜單管理",
                    "menuLevel": 2,
                    "route": "/menu/manage",
                    "authMenuList": []
                }
            ]
        },
        {
            "id": 1,
            "parentId": 0,
            "menuName": "首頁",
            "menuLevel": 1,
            "route": "/index",
            "authMenuList": []
        }
    ]
}

以上就是Java中實現(xiàn)樹形菜單的兩種方式的詳細內(nèi)容,更多關于Java實現(xiàn)樹形菜單的資料請關注腳本之家其它相關文章!

相關文章

  • Springboot項目登錄校驗功能實現(xiàn)

    Springboot項目登錄校驗功能實現(xiàn)

    本文介紹了Web登錄校驗的重要性,對比了Cookie、Session和JWT三種會話技術,分析其優(yōu)缺點,并講解了過濾器與攔截器的統(tǒng)一攔截方案,推薦使用JWT結(jié)合攔截器實現(xiàn)安全、高效的登錄校驗,下面通過實例代碼講解Springboot項目的登錄校驗實現(xiàn),感興趣的朋友一起看看吧
    2025-08-08
  • 一篇文章帶你了解Java SpringMVC返回null

    一篇文章帶你了解Java SpringMVC返回null

    這篇文章主要介紹了Spring MVC返回null,文中講的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2021-10-10
  • 一文帶你了解RabbitMQ消息轉(zhuǎn)換器

    一文帶你了解RabbitMQ消息轉(zhuǎn)換器

    這篇文章主要為大家詳細介紹了RabbitMQ中消息轉(zhuǎn)換器的相關知識,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以了解一下
    2023-04-04
  • SpringBoot中MybatisX插件的簡單使用教程(圖文)

    SpringBoot中MybatisX插件的簡單使用教程(圖文)

    MybatisX 是一款基于 IDEA 的快速開發(fā)插件,方便在使用mybatis以及mybatis-plus開始時簡化繁瑣的重復操作,本文主要介紹了SpringBoot中MybatisX插件的簡單使用教程,感興趣的可以了解一下
    2023-06-06
  • SpringBoot獲取ApplicationContext的3種方式

    SpringBoot獲取ApplicationContext的3種方式

    這篇文章主要為大家詳細介紹了SpringBoot獲取ApplicationContext的3種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • java token生成和校驗的實例代碼

    java token生成和校驗的實例代碼

    這篇文章主要介紹了java token生成和校驗的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Java中Stream?Filter多條件篩選過濾代碼舉例

    Java中Stream?Filter多條件篩選過濾代碼舉例

    這篇文章主要給大家介紹了關于Java中Stream?Filter多條件篩選過濾的相關資料,Java Stream中的filter方法可以使用多個條件來過濾數(shù)據(jù),文中給出了詳細的代碼示例,需要的朋友可以參考下
    2023-12-12
  • SpringBoot?支付渠道實現(xiàn)統(tǒng)一的支付服務示例

    SpringBoot?支付渠道實現(xiàn)統(tǒng)一的支付服務示例

    本文提出了一種基于工廠模式和策略模式的多渠道支付封裝方案,該設計采用策略模式實現(xiàn)各支付渠道(微信、支付寶、銀聯(lián))的統(tǒng)一接口,通過工廠模式動態(tài)創(chuàng)建支付策略,感興趣的可以了解一下
    2026-03-03
  • Java快速實現(xiàn)Word轉(zhuǎn)圖片功能的多種方法與實踐

    Java快速實現(xiàn)Word轉(zhuǎn)圖片功能的多種方法與實踐

    在軟件開發(fā)中,轉(zhuǎn)換文件格式是一個常見需求,尤其是將Word文件轉(zhuǎn)換為圖片格式,這在報表生成、文檔預覽、自動化處理等場景中非常有用,在 Java 中實現(xiàn)Word轉(zhuǎn)圖片的功能有多種思路,以下將介紹幾種常見的實現(xiàn)方式,并探討它們的優(yōu)缺點,需要的朋友可以參考下
    2025-08-08
  • JAVA使用TreeMap對字符串進行排序

    JAVA使用TreeMap對字符串進行排序

    這篇文章主要介紹了JAVA使用TreeMap對字符串進行排序,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02

最新評論

年辖:市辖区| 江津市| 乐都县| 武鸣县| 德惠市| 古蔺县| 巫溪县| 昌平区| 将乐县| 河曲县| 石景山区| 饶阳县| 鲜城| 万荣县| 稷山县| 蒲城县| 东源县| 鹿邑县| 怀柔区| 宝兴县| 兴化市| 稻城县| 克山县| 台江县| 江山市| 攀枝花市| 辽源市| 扶绥县| 基隆市| 原阳县| 肥西县| 靖西县| 新竹县| 铁岭市| 鹿邑县| 博客| 孝昌县| 砀山县| 昌都县| 香河县| 增城市|