菜单树的三种实现形式(递归,for, 队列)

菜单树在软件开发中经常用于展示层级结构的数据,例如网站导航菜单或文件系统。在实际开发中,我们常常需要考虑不同的数据结构和算法来实现这种层级关系的展示。本文将介绍菜单树的三种常见实现方式:递归、for循环和队列,帮助读者更好地理解和选择合适的方法。

准备

示例数据库

sql 复制代码
create table menu
(
    id        int auto_increment
        primary key,
    name      varchar(100) not null,
    parent_id int          null
);

create index parent_id
    on menu (parent_id);
复制代码

Mapper

java 复制代码
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {
    @Select("SELECT id, name, parent_id FROM menu WHERE parent_id = #{parentId}")
    List<Menu> findMenusByParentId(@Param("parentId") Integer parentId);

}

Service

java 复制代码
public interface MenuService {
    List<Menu> findMenusByParentId(Integer parentId);
}

Impl

java 复制代码
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
    @Autowired
    private MenuMapper menuMapper;
    @Override
    public List<Menu> findMenusByParentId(Integer parentId) {
        return menuMapper.findMenusByParentId(parentId);
    }
}

1. 递归实现

java 复制代码
@RestController
@RequestMapping("/menus")
public class MenuController {

    @Autowired
    private MenuService menuService;

    // 返回根菜单列表及其子菜单
    @GetMapping("/tree")
    public List<Menu> getMenuTree(Integer parentId) {
        List<Menu> rootMenus = menuService.findMenusByParentId(parentId); // 获取顶级菜单
        buildMenuTree(rootMenus); // 构建树形结构
        return rootMenus;
    }

    // 递归方法,构建菜单树
    private void buildMenuTree(List<Menu> menus) {
        for (Menu menu : menus) {
            List<Menu> children = menuService.findMenusByParentId(menu.getId());
            if (!children.isEmpty()) {
                menu.setChildren(children); // 设置子菜单列表
                buildMenuTree(children); // 递归构建子树
            }
        }
    }
}

2.For实现

java 复制代码
@RestController
@RequestMapping("/treemenus")
public class MenuNotRecursionUseForController {

    @Resource
    private MenuService menuService;
    @GetMapping("/tree")
    public List<Menu> getMenuTree(Integer parentId) {
        List<Menu> rootMenus = menuService.findMenusByParentId(parentId); // 获取顶级菜单列表

        // 创建一个 Map 用于存放每个菜单的 ID 和对应的菜单对象
        Map<Integer, Menu> menuMap = new HashMap<>();
        for (Menu menu : rootMenus) {
            menu.setChildren(new ArrayList<>()); // 初始化子菜单列表
            menuMap.put(menu.getId(), menu); // 将菜单对象放入 map 中,以便后续快速访问
        }

        // 遍历第二层菜单,逐层构建树形结构
        for (Menu menu : rootMenus) {
            // 查询当前菜单的所有子菜单(第二层菜单)
            List<Menu> children = menuService.findMenusByParentId(menu.getId());
            for (Menu child : children) {
                // 将第二层菜单添加到父菜单的子菜单列表中
                Menu parentMenu = menuMap.get(child.getParentId());
                if (parentMenu != null) {
                    parentMenu.getChildren().add(child);

                    // 查询当前第二层菜单的所有子菜单(第三层菜单)
                    List<Menu> grandchildren = menuService.findMenusByParentId(child.getId());
                    if (child.getChildren() == null) {
                        child.setChildren(new ArrayList<>()); // 初始化第三层子菜单列表
                    }
                    for (Menu grandchild : grandchildren) {
                        // 将第三层菜单添加到当前第二层菜单的子菜单列表中
                        child.getChildren().add(grandchild);
                    }
                }
            }
        }

        // 返回构建好的根菜单列表
        return rootMenus;
    }


}

3. 队列实现

java 复制代码
@RestController
@RequestMapping("/treemenustwo")
public class MenuNotRecursionUseQueueController {
        @Autowired
        private MenuService menuService;

        @GetMapping("/tree")
        public List<Menu> getMenuTree(Integer parentId) {
            List<Menu> rootMenus = menuService.findMenusByParentId(parentId); // 获取顶级菜单列表

            // 使用队列来进行广度优先搜索
            Queue<Menu> queue = new LinkedList<>(rootMenus);

            while (!queue.isEmpty()) {
                Menu currentMenu = queue.poll(); // 出队当前菜单

                // 查询当前菜单的所有子菜单
                List<Menu> children = menuService.findMenusByParentId(currentMenu.getId());

                // 将子菜单加入当前菜单的 children 列表中
                currentMenu.setChildren(children);

                // 将子菜单加入队列,以便后续处理其子菜单
                queue.addAll(children);
            }

            return rootMenus; // 返回构建好的树形结构
        }

    }
相关推荐
千维百策6668 分钟前
SRE 中如何消除琐务:提升工程效率与服务可靠性的实践方法
数据库
卓怡学长16 分钟前
w266基于spring boot + vue 圣地延安美食乐享系统
java·数据库·vue.js·spring boot·spring·intellij-idea
名不经传的养虾人1 小时前
从0到1:企业级AI项目迭代日记 Vol.65|最危险的故障不是崩溃,是悄悄换掉了正确答案
数据库·人工智能·ai编程·ai-agent·企业ai
程序员佳佳1 小时前
模型网关灰度不是调百分比:把放量、观测和回滚做成账本
java·数据库·人工智能·redis·gpt·aigc·embedding
绩隐金1 小时前
用AI开了本小说 九折归潮
数据库
heimeiyingwang1 小时前
【架构实战】MySQL索引优化:从慢查询到毫秒响应
数据库·mysql·架构
得闲喝茶1 小时前
跨表数据匹配——VLOOKUP、XLOOKUP
大数据·数据库·笔记·信息可视化·数据分析·excel
snow@li1 小时前
数据库:如何设计最佳索引 / 数据库最佳索引设计指南
数据库
渣渣灰飞2 小时前
MySQL 系统学习 第二阶段 第四章:DCL(Data Control Language)第二节:权限管理与角色(Role)
数据库·学习·mysql
肖永威2 小时前
金仓数据库麒麟环境SQL终端使用入门实践(增删改查篇)
数据库·sql·金仓数据库