Java案例怎么构建菜单树形?

wen java案例 13

本文目录导读:

Java案例怎么构建菜单树形?

  1. 基本数据模型定义
  2. 方法一:递归构建树形
  3. 方法二:使用Map优化性能
  4. 方法三:Stream API方式
  5. 完整的测试示例
  6. 输出结果示例
  7. Spring Boot中的实际应用

我来介绍几种在Java中构建菜单树形结构的常见方法:

基本数据模型定义

// 菜单实体类
public class Menu {
    private Long id;
    private String name;
    private Long parentId;
    private String url;
    private Integer sort;
    private List<Menu> children; // 子菜单列表
    // getters/setters 省略
    public Menu(Long id, String name, Long parentId, String url, Integer sort) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
        this.url = url;
        this.sort = sort;
    }
}

方法一:递归构建树形

public class MenuTreeBuilder {
    /**
     * 递归构建菜单树
     */
    public static List<Menu> buildTree(List<Menu> menuList) {
        List<Menu> tree = new ArrayList<>();
        // 找出所有根节点(parentId为null或0)
        for (Menu menu : menuList) {
            if (menu.getParentId() == null || menu.getParentId() == 0) {
                tree.add(findChildren(menu, menuList));
            }
        }
        // 按排序字段排序
        tree.sort(Comparator.comparing(Menu::getSort));
        return tree;
    }
    private static Menu findChildren(Menu parent, List<Menu> menuList) {
        List<Menu> children = new ArrayList<>();
        for (Menu menu : menuList) {
            if (parent.getId().equals(menu.getParentId())) {
                children.add(findChildren(menu, menuList));
            }
        }
        // 子节点排序
        children.sort(Comparator.comparing(Menu::getSort));
        parent.setChildren(children);
        return parent;
    }
}

方法二:使用Map优化性能

public class MenuTreeBuilderV2 {
    /**
     * 使用Map优化,O(n)复杂度
     */
    public static List<Menu> buildTreeOptimized(List<Menu> menuList) {
        List<Menu> tree = new ArrayList<>();
        Map<Long, Menu> menuMap = new HashMap<>();
        // 1. 先转换为Map
        for (Menu menu : menuList) {
            menu.setChildren(new ArrayList<>());
            menuMap.put(menu.getId(), menu);
        }
        // 2. 构建树形关系
        for (Menu menu : menuList) {
            Long parentId = menu.getParentId();
            if (parentId != null && menuMap.containsKey(parentId)) {
                // 如果有父节点,添加到父节点的children中
                Menu parent = menuMap.get(parentId);
                parent.getChildren().add(menu);
            } else {
                // 没有父节点或父节点不在列表中,作为根节点
                tree.add(menu);
            }
        }
        // 3. 排序
        sortMenuTree(tree);
        return tree;
    }
    private static void sortMenuTree(List<Menu> menus) {
        menus.sort(Comparator.comparing(Menu::getSort));
        for (Menu menu : menus) {
            if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {
                sortMenuTree(menu.getChildren());
            }
        }
    }
}

方法三:Stream API方式

public class MenuTreeBuilderV3 {
    /**
     * 使用Stream API构建树
     */
    public static List<Menu> buildTreeWithStream(List<Menu> menuList) {
        Map<Long, List<Menu>> groupByParent = menuList.stream()
            .collect(Collectors.groupingBy(
                menu -> menu.getParentId() != null ? menu.getParentId() : 0L
            ));
        return menuList.stream()
            .filter(menu -> menu.getParentId() == null || menu.getParentId() == 0)
            .peek(menu -> menu.setChildren(getChildren(menu.getId(), groupByParent)))
            .sorted(Comparator.comparing(Menu::getSort))
            .collect(Collectors.toList());
    }
    private static List<Menu> getChildren(Long parentId, 
                                          Map<Long, List<Menu>> groupByParent) {
        List<Menu> children = groupByParent.getOrDefault(parentId, new ArrayList<>());
        children.sort(Comparator.comparing(Menu::getSort));
        children.forEach(child -> 
            child.setChildren(getChildren(child.getId(), groupByParent))
        );
        return children;
    }
}

完整的测试示例

public class MenuTreeDemo {
    public static void main(String[] args) {
        // 模拟数据库查询出的菜单列表
        List<Menu> menuList = Arrays.asList(
            new Menu(1L, "系统管理", null, null, 1),
            new Menu(2L, "用户管理", 1L, "/user", 1),
            new Menu(3L, "角色管理", 1L, "/role", 2),
            new Menu(4L, "权限管理", 1L, "/permission", 3),
            new Menu(5L, "业务管理", null, null, 2),
            new Menu(6L, "订单管理", 5L, "/order", 1),
            new Menu(7L, "商品管理", 5L, "/product", 2),
            new Menu(8L, "商品列表", 7L, "/product/list", 1),
            new Menu(9L, "商品分类", 7L, "/product/category", 2)
        );
        // 构建树形
        List<Menu> tree = MenuTreeBuilderV2.buildTreeOptimized(menuList);
        // 输出树形结构
        printMenuTree(tree, 0);
    }
    private static void printMenuTree(List<Menu> menus, int level) {
        String indent = "  ".repeat(level);
        for (Menu menu : menus) {
            System.out.println(indent + "├─ " + menu.getName() + 
                             (menu.getUrl() != null ? " (" + menu.getUrl() + ")" : ""));
            if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {
                printMenuTree(menu.getChildren(), level + 1);
            }
        }
    }
}

输出结果示例

├─ 系统管理
  ├─ 用户管理 (/user)
  ├─ 角色管理 (/role)
  ├─ 权限管理 (/permission)
├─ 业务管理
  ├─ 订单管理 (/order)
  ├─ 商品管理 (/product)
    ├─ 商品列表 (/product/list)
    ├─ 商品分类 (/product/category)

Spring Boot中的实际应用

@RestController
@RequestMapping("/api/menu")
public class MenuController {
    @Autowired
    private MenuService menuService;
    @GetMapping("/tree")
    public Result<List<Menu>> getMenuTree() {
        List<Menu> menuList = menuService.list(); // 从数据库查询所有菜单
        List<Menu> tree = MenuTreeBuilderV2.buildTreeOptimized(menuList);
        return Result.success(tree);
    }
    /**
     * 返回EasyUI、Layui等前端框架需要的格式
     */
    @GetMapping("/easyui-tree")
    public Result<List<EasyUITreeNode>> getEasyUITree() {
        List<Menu> menuList = menuService.list();
        List<EasyUITreeNode> tree = convertToEasyUITree(menuList);
        return Result.success(tree);
    }
    private List<EasyUITreeNode> convertToEasyUITree(List<Menu> menuList) {
        // 适配前端框架的格式转换
        // ...
    }
}

建议:

  • 数据量小时使用递归方式,代码清晰易懂
  • 数据量大时使用Map方式,性能更好
  • 可以结合缓存机制,减少频繁构建树形结构
  • 注意处理循环引用的问题

抱歉,评论功能暂时关闭!