多级菜单(无限级菜单实现)

实现多级菜单

方法一: 使用List结合Map实现 (效率最高,可无限扩展)

核心:
id(主键列)作为Map的key
Menu作为Map的value

这里使用模拟数据实现

import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.*;

public class MenuDemo implements Serializable {
 	
 	// 模拟数据库字段
    static class Menu implements Serializable {
        public Long id;

        public Long parentId;

        public String name;

        public List<Menu> menuList;

        public Menu(Long id, Long parentId, String name) {
            this.id = id;
            this.parentId = parentId;
            this.name = name;
        }
		//方便打印
        @Override
        public String toString() {
            return "Menu{" +
                    "id=" + id +
                    ", parentId=" + parentId +
                    ", name='" + name + '\'' +
                    ", menuList=" + menuList +
                    '}';
        }
    }


    public static void main(String[] args) {

        // 造数据
        Menu menu1 = new Menu(1L, null, "一级菜单1");

        Menu menu2 = new Menu(2L, null, "一级菜单2");

        Menu menu3 = new Menu(3L, 1L, "二级菜单1");

        Menu menu4 = new Menu(4L, 2L, "二级菜单2");

        Menu menu5 = new Menu(5L, 3L, "三级菜单1");

        Menu menu6 = new Menu(6L, 4L, "三级菜单2");
        Menu menu7 = new Menu(7L, 5L, "四级菜单2");
        Menu menu8 = new Menu(8L, 6L, "四级菜单2");

        List<Menu> menuList = new ArrayList<>();
        menuList.add(menu1);
        menuList.add(menu2);
        menuList.add(menu3);
        menuList.add(menu4);
        menuList.add(menu5);
        menuList.add(menu6);
        menuList.add(menu7);
        menuList.add(menu8);


        /* 将所有级别菜单装入Map */
        Map<Long, Menu> menuMap = new HashMap<>();
        for (Menu menu : menuList) {
            menuMap.put(menu.id, menu);
        }
        // 寻找自己的父节点
        for (Menu menu : menuList) {
            Long parentId = menu.parentId;
            if (parentId == null) {
                continue;
            }
            // 取map中当前菜单的上级菜单
            Menu parentMenu = menuMap.get(parentId);
            // 上级菜单中的子菜单
            List<Menu> childMenuList = parentMenu.menuList;
            // 子菜单为空
            if (CollectionUtils.isEmpty(childMenuList)) {
                childMenuList = new ArrayList<>();
                /**
                 * 这一步是这个方法的灵魂  使用引用传值将 childMenuList的地址传		     给parentMenu
                 * 下边想要添加只需要继续操作childMenuList即可
                 * 若是使用真是数据库 可改为
                 * parentMenu.setMenuList(childMenuList);
                 * 
                 */
                parentMenu.menuList = childMenuList;
            }
            childMenuList.add(menu);
        }
        // 找到根节点输出
        List<Menu> result = new ArrayList<>();
        for (Menu menu : menuList) {
            if (menu.parentId == null) {
                result.add(menu);
            }
        }

        for (Menu menu:result) {
            System.out.println(menu.toString());
        }
        /*System.out.println(JSONUtils.write(result));*/
    }
}

方法二: 类似递归思想(可无限扩展):

@Data
public class MenuResultDTO implements Serializable {
    private Integer id;
    private Integer parentId;
    private String name;
    private Integer level;
    private Integer sortOrder;
    private Integer isShow;
    private String image;
    private String status;
    private Date createTime;
    private Date updateTime;
    private BigInteger createBy;
    private BigInteger updateBy;
    private List<MenuResultDTO> menuResultDTOList;
}
/**
     * 多级菜单查询(不可越级开启菜单)
     *
     * @param menuParamsDTO
     * @return
     */
    @Override
    public List<MenuResultDTO> queryMenu(MenuParamsDTO menuParamsDTO) {
    // 所有菜单查询结果
        List<MenuResultDTO> allMenuList = menuMapper.query(menuParamsDTO);
        if (allMenuList == null) {
        // 自定义异常 也可直接return 
            throw new GlobalException(GlobalEnum.MSG_NOTFULL);
        }
        // 菜单归类(把所有菜单分为一二三级......依次装入Map)
        Map<Integer, List<MenuResultDTO>> mapMenuList = this.buildMenusLevel(allMenuList);
        // 菜单拼装 调用分类整理各级菜单方法
        Map<Integer, List<MenuResultDTO>> integerListMap = this.buildMultiplyMenu(mapMenuList);
        // 组装完成后Map中只剩下一级菜单
        List<MenuResultDTO> menuResultDTOS = integerListMap.get(1);
        return menuResultDTOS;
    }

    /**
     * 分类整理各级别菜单
     *
     * @param allMenuList
     * @return
     */
    private Map<Integer, List<MenuResultDTO>> buildMenusLevel(List<MenuResultDTO> allMenuList) {
        // 存放结果
        Map<Integer, List<MenuResultDTO>> menuListMap = new HashMap<>(16);
        Iterator<MenuResultDTO> iterator = allMenuList.iterator();
        while (iterator.hasNext()) {
        // map中的每一个菜单
            MenuResultDTO menuResultDTO = iterator.next();
            // 获取每个菜单的等级 方便划分
            List<MenuResultDTO> list = menuListMap.get(menuResultDTO.getLevel());
            // 不存在该级菜单
            if (list == null) {
                list = new ArrayList<>();
                list.add(menuResultDTO);
                menuListMap.put(menuResultDTO.getLevel(), list);
            } else {
                List<MenuResultDTO> menuResultDTOList = menuListMap.get(menuResultDTO.getLevel());
                // 由于list引用传值不需要put
                menuResultDTOList.add(menuResultDTO);
            }
        }
        return menuListMap;
    }

    /**
     * 多级菜单拼装(核心)
     *
     * @param mapMenuList
     * @return
     */
    private Map<Integer, List<MenuResultDTO>> buildMultiplyMenu(Map<Integer, List<MenuResultDTO>> mapMenuList) {
        // 多级菜单组装结果
        Map<Integer, List<MenuResultDTO>> menuListMap = new HashMap(16);
         /**
	     * map中有几个级别就循环几次
	     * 因为实现逻辑是把下一级所有菜单往上一级对应菜单装填
	     * 例如: 四级 全部装到对应的三级菜单中
	     */
        for (int i = mapMenuList.size(); i >= 1; i--) {
            // i-1代表最后一级菜单上一级
            List<MenuResultDTO> beforeMenuList = mapMenuList.get(i - 1);
            if (beforeMenuList == null) {
                return mapMenuList;
            } else {
                Iterator<MenuResultDTO> beforeIterator = beforeMenuList.iterator();
                while (beforeIterator.hasNext()) {
                    // 最后一级菜单上级菜单的单个菜单
                    MenuResultDTO beforeLevel = beforeIterator.next();
                    // 创建子菜单集合 用来存放当前菜单的子菜单
                    List<MenuResultDTO> sonMenuList = new ArrayList();
                    // i代表最后一级菜单集合
                    List<MenuResultDTO> lastMenuList = mapMenuList.get(i);
                    Iterator<MenuResultDTO> lastIterator = lastMenuList.iterator();
                    while (lastIterator.hasNext()) {
                        MenuResultDTO lastLevel = lastIterator.next();
                        if (beforeLevel.getId().equals(lastLevel.getParentId())) {
                            sonMenuList.add(lastLevel);
                        }
                    }
                    beforeLevel.setMenuResultDTOList(sonMenuList);
                    // 循环组装完成后只剩一级菜单
                    menuListMap.put(1, beforeMenuList);
                }
            }
        }

        return menuListMap;
    }

总结

方法一:时间复杂度O(n) 思路清晰易懂 代码可读性强
方法二:时间复杂的O(n^3) 逻辑复杂 代码可读性差

一周小结1

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值