本文目录导读:

我来介绍几种在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方式,性能更好
- 可以结合缓存机制,减少频繁构建树形结构
- 注意处理循环引用的问题
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播