实现多级菜单
方法一: 使用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