菜单树的三种实现形式(递归,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; // 返回构建好的树形结构
        }

    }
相关推荐
AAA修煤气灶刘哥7 小时前
后端人速藏!数据库PD建模避坑指南
数据库·后端·mysql
RestCloud11 小时前
揭秘 CDC 技术:让数据库同步快人一步
数据库·api
得物技术14 小时前
MySQL单表为何别超2000万行?揭秘B+树与16KB页的生死博弈|得物技术
数据库·后端·mysql
可涵不会debug18 小时前
【IoTDB】时序数据库选型指南:工业大数据场景下的技术突围
数据库·时序数据库
ByteBlossom18 小时前
MySQL 面试场景题之如何处理 BLOB 和CLOB 数据类型?
数据库·mysql·面试
麦兜*18 小时前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
Slaughter信仰18 小时前
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第十章知识点问答(10题)
java·jvm·数据库
麦兜*18 小时前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring
-Xie-18 小时前
Mysql杂志(十六)——缓存池
数据库·mysql·缓存