组装成树

news/2025/12/1 11:20:50/文章来源:https://www.cnblogs.com/chaoshang8/p/19292103

我来为你提供几种Java实现菜单树形结构的方案:

方案一:使用递归构建树形结构

// 菜单实体类
@Data
public class Menu {
private String code;
private String parentCode;
private String name;
private Integer sort;
private List

children;

// 构造方法、getter、setter省略

}

// 树形结构构建工具类
@Component
public class MenuTreeBuilder {

/*** 构建菜单树*/
public List<Menu> buildTree(List<Menu> menuList) {if (menuList == null || menuList.isEmpty()) {return new ArrayList<>();}// 找出所有根节点(parentCode为null或空字符串)List<Menu> rootMenus = menuList.stream().filter(menu -> menu.getParentCode() == null || menu.getParentCode().isEmpty()).sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());// 为每个根节点递归设置子节点rootMenus.forEach(this::setChildren);return rootMenus;
}/*** 递归设置子节点*/
private void setChildren(Menu parentMenu) {List<Menu> children = menuList.stream().filter(menu -> parentMenu.getCode().equals(menu.getParentCode())).sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());if (!children.isEmpty()) {parentMenu.setChildren(children);// 递归设置孙子节点children.forEach(this::setChildren);}
}

}

方案二:使用Map优化查询效率

@Service
public class MenuService {

/*** 使用Map优化构建树形结构*/
public List<Menu> buildTreeWithMap(List<Menu> menuList) {if (menuList == null || menuList.isEmpty()) {return new ArrayList<>();}// 将列表转为Map,key为code,value为Menu对象Map<String, Menu> menuMap = menuList.stream().collect(Collectors.toMap(Menu::getCode, menu -> menu));List<Menu> tree = new ArrayList<>();for (Menu menu : menuList) {String parentCode = menu.getParentCode();if (parentCode == null || parentCode.isEmpty()) {// 根节点tree.add(menu);} else {// 子节点,找到父节点并添加到children中Menu parent = menuMap.get(parentCode);if (parent != null) {if (parent.getChildren() == null) {parent.setChildren(new ArrayList<>());}parent.getChildren().add(menu);}}}// 对每个层级的节点进行排序sortTree(tree);return tree;
}/*** 递归排序树节点*/
private void sortTree(List<Menu> menus) {if (menus == null || menus.isEmpty()) {return;}menus.sort(Comparator.comparing(Menu::getSort));for (Menu menu : menus) {if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {sortTree(menu.getChildren());}}
}

}

方案三:使用Stream API的简洁版本

public class MenuTreeUtils {

/*** 使用Stream构建树形结构*/
public static List<Menu> buildTreeStream(List<Menu> menus) {Map<String, List<Menu>> parentMap = menus.stream().filter(menu -> menu.getParentCode() != null).collect(Collectors.groupingBy(Menu::getParentCode));return menus.stream().filter(menu -> menu.getParentCode() == null || menu.getParentCode().isEmpty()).peek(root -> setChildrenRecursively(root, parentMap)).sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());
}private static void setChildrenRecursively(Menu parent, Map<String, List<Menu>> parentMap) {List<Menu> children = parentMap.getOrDefault(parent.getCode(), new ArrayList<>());if (!children.isEmpty()) {children.sort(Comparator.comparing(Menu::getSort));parent.setChildren(children);children.forEach(child -> setChildrenRecursively(child, parentMap));}
}

}

方案四:完整的Spring Boot使用示例

@RestController
@RequestMapping("/api/menus")
public class MenuController {

@Autowired
private MenuService menuService;@GetMapping("/tree")
public ResponseEntity<List<Menu>> getMenuTree() {try {// 模拟从数据库查询菜单数据List<Menu> menuList = getMenuListFromDB();// 构建树形结构List<Menu> menuTree = menuService.buildTreeWithMap(menuList);return ResponseEntity.ok(menuTree);} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}
}private List<Menu> getMenuListFromDB() {// 这里模拟数据库查询结果List<Menu> menus = new ArrayList<>();// 添加测试数据menus.add(createMenu("1", null, "系统管理", 1));menus.add(createMenu("2", "1", "用户管理", 1));menus.add(createMenu("3", "1", "角色管理", 2));menus.add(createMenu("4", "2", "用户列表", 1));menus.add(createMenu("5", "2", "添加用户", 2));menus.add(createMenu("6", null, "业务管理", 2));menus.add(createMenu("7", "6", "订单管理", 1));return menus;
}private Menu createMenu(String code, String parentCode, String name, Integer sort) {Menu menu = new Menu();menu.setCode(code);menu.setParentCode(parentCode);menu.setName(name);menu.setSort(sort);return menu;
}

}

方案五:支持多种根节点标识的通用版本

public class AdvancedMenuTreeBuilder {

/*** 构建树形结构 - 支持自定义根节点判断条件*/
public static <T extends TreeNode> List<T> buildTree(List<T> nodes, RootNodePredicate<T> rootPredicate) {if (nodes == null || nodes.isEmpty()) {return new ArrayList<>();}Map<String, T> nodeMap = nodes.stream().collect(Collectors.toMap(TreeNode::getCode, node -> node));List<T> tree = new ArrayList<>();for (T node : nodes) {String parentCode = node.getParentCode();if (rootPredicate.isRoot(node)) {tree.add(node);} else if (parentCode != null && nodeMap.containsKey(parentCode)) {T parent = nodeMap.get(parentCode);if (parent.getChildren() == null) {parent.setChildren(new ArrayList<>());}parent.getChildren().add(node);}}// 排序sortTree(tree);return tree;
}private static <T extends TreeNode> void sortTree(List<T> nodes) {nodes.sort(Comparator.comparing(TreeNode::getSort));for (T node : nodes) {if (node.getChildren() != null && !node.getChildren().isEmpty()) {sortTree(node.getChildren());}}
}/*** 根节点判断接口*/
@FunctionalInterface
public interface RootNodePredicate<T> {boolean isRoot(T node);
}

}

使用示例

// 使用通用版本
List

menus = getMenuListFromDB();

// 方式1:parentCode为null的是根节点
List

tree1 = AdvancedMenuTreeBuilder.buildTree(menus,
node -> node.getParentCode() == null);

// 方式2:parentCode为0或空字符串的是根节点
List

tree2 = AdvancedMenuTreeBuilder.buildTree(menus,
node -> node.getParentCode() == null || "0".equals(node.getParentCode()));

// 方式3:自定义根节点逻辑
List

tree3 = AdvancedMenuTreeBuilder.buildTree(menus,
node -> "ROOT".equals(node.getType()));

性能对比和建议

  1. 方案一(递归):代码直观,但大数据量时可能栈溢出

  2. 方案二(Map优化):性能最好,推荐使用

  3. 方案三(Stream):代码简洁,适合中小数据量

  4. 方案五(通用版):最灵活,可复用性强

推荐使用方法二,它在性能和代码可读性之间取得了很好的平衡。

是的,Hutool提供了非常便捷的树形结构构建工具类!主要有以下几种方式:

方案一:使用 TreeUtil 构建树形结构

  1. 基础用法

// 引入依赖
//
// cn.hutool
// hutool-all
// 5.8.16
//

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;

// 菜单实体类实现 TreeNode 接口
@Data
public class Menu implements TreeNode {
private String id; // 对应 code
private String parentId; // 对应 parent_code
private String name;
private Integer sort;
private String weight; // 权重字段,用于排序

@Override
public String getId() {return this.id;
}@Override
public String getName() {return this.name;
}@Override
public String getParentId() {return this.parentId;
}@Override
public List<TreeNode<String>> getChildren() {return (List<TreeNode<String>>) (Object) this.children;
}@Override
public void setChildren(List<TreeNode<String>> children) {this.children = (List<Menu>) children;
}@Override
public Comparable<?> getWeight() {return this.sort;
}private List<Menu> children;

}

  1. 构建树形结构

@Service
public class MenuService {

public List<Tree<String>> buildMenuTree() {// 模拟从数据库查询的菜单数据List<Menu> menuList = getMenuListFromDB();// 使用 Hutool TreeUtil 构建树List<Tree<String>> treeList = TreeUtil.build(menuList, "0"); // "0" 表示根节点的 parentIdreturn treeList;
}public List<Tree<String>> buildMenuTreeCustom() {List<Menu> menuList = getMenuListFromDB();// 自定义配置构建树List<Tree<String>> treeList = TreeUtil.build(menuList, "0",(treeNode, tree) -> {// 自定义节点信息设置tree.setId(treeNode.getId());tree.setName(treeNode.getName());tree.setParentId(treeNode.getParentId());tree.putExtra("sort", treeNode.getSort()); // 额外属性});return treeList;
}private List<Menu> getMenuListFromDB() {List<Menu> menus = new ArrayList<>();menus.add(createMenu("1", "0", "系统管理", 1));menus.add(createMenu("2", "1", "用户管理", 1));menus.add(createMenu("3", "1", "角色管理", 2));menus.add(createMenu("4", "2", "用户列表", 1));menus.add(createMenu("5", "2", "添加用户", 2));menus.add(createMenu("6", "0", "业务管理", 2));return menus;
}private Menu createMenu(String id, String parentId, String name, Integer sort) {Menu menu = new Menu();menu.setId(id);menu.setParentId(parentId);menu.setName(name);menu.setSort(sort);return menu;
}

}

方案二:使用 Map 数据直接构建

public class MenuTreeDemo {

public void buildTreeFromMap() {// 直接使用 Map 数据构建,无需创建实体类List<Map<String, Object>> menuData = new ArrayList<>();Map<String, Object> menu1 = new HashMap<>();menu1.put("id", "1");menu1.put("parentId", "0");menu1.put("name", "系统管理");menu1.put("sort", 1);Map<String, Object> menu2 = new HashMap<>();menu2.put("id", "2");menu2.put("parentId", "1");menu2.put("name", "用户管理");menu2.put("sort", 1);menuData.add(menu1);menuData.add(menu2);// 构建树List<Tree<String>> trees = TreeUtil.build(menuData, "0",(treeNode, tree) -> {tree.setId(treeNode.get("id").toString());tree.setName(treeNode.get("name").toString());tree.setParentId(treeNode.get("parentId").toString());tree.putExtra("sort", treeNode.get("sort"));});System.out.println(JSONUtil.toJsonPrettyStr(trees));
}

}

方案三:使用注解方式(推荐)

// 使用 Hutool 的 @Alias 注解简化字段映射
@Data
public class MenuVO {
@Alias("code") // 对应原始数据的 code 字段
private String id;

@Alias("parent_code") // 对应原始数据的 parent_code 字段
private String parentId;@Alias("name")
private String name;@Alias("sort")
private Integer sort;private List<MenuVO> children;

}

public class AnnotationDemo {

public void buildTreeWithAnnotation() {// 原始数据(数据库查询结果)List<Map<String, Object>> rawData = new ArrayList<>();Map<String, Object> data1 = new HashMap<>();data1.put("code", "1");data1.put("parent_code", "0");data1.put("name", "系统管理");data1.put("sort", 1);Map<String, Object> data2 = new HashMap<>();data2.put("code", "2");data2.put("parent_code", "1");data2.put("name", "用户管理");data2.put("sort", 1);rawData.add(data1);rawData.add(data2);// 转换为 MenuVO 列表List<MenuVO> menuList = CollUtil.map(rawData, map -> {MenuVO vo = new MenuVO();BeanUtil.fillBeanWithMap(map, vo, true);return vo;}, true);// 构建树List<Tree<String>> trees = TreeUtil.build(menuList, "0");System.out.println(JSONUtil.toJsonPrettyStr(trees));
}

}

方案四:在 Spring Boot Controller 中使用

@RestController
@RequestMapping("/api/menus")
public class MenuController {

@GetMapping("/tree")
public ResponseEntity<List<Tree<String>>> getMenuTree() {try {List<Menu> menuList = getMenuListFromDB();// 一行代码搞定树形结构!List<Tree<String>> menuTree = TreeUtil.build(menuList, "0");return ResponseEntity.ok(menuTree);} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}
}@GetMapping("/tree-with-config")
public ResponseEntity<List<Tree<String>>> getMenuTreeWithConfig() {List<Menu> menuList = getMenuListFromDB();// 带配置的构建List<Tree<String>> trees = TreeUtil.build(menuList, "0",(treeNode, tree) -> {// 可以设置节点文本格式化等tree.setName(treeNode.getName() + "(" + treeNode.getId() + ")");tree.putExtra("type", "menu");tree.putExtra("createTime", new Date());},(treeNode1, treeNode2) -> {// 自定义排序规则Integer sort1 = (Integer) treeNode1.getExtra().get("sort");Integer sort2 = (Integer) treeNode2.getExtra().get("sort");return Integer.compare(sort1, sort2);});return ResponseEntity.ok(trees);
}

}

方案五:其他实用方法

public class TreeUtilOtherMethods {

/*** 获取指定节点的所有子节点ID*/
public void getAllChildIds() {List<Menu> menuList = getMenuListFromDB();List<Tree<String>> trees = TreeUtil.build(menuList, "0");Tree<String> root = trees.get(0);List<Object> childIds = TreeUtil.getAllChildIdByBreadthFirst(root);System.out.println("所有子节点ID: " + childIds);
}/*** 根据ID查找节点*/
public void findNodeById() {List<Menu> menuList = getMenuListFromDB();List<Tree<String>> trees = TreeUtil.build(menuList, "0");Tree<String> found = TreeUtil.getTreeNodeById(trees, "2");System.out.println("找到节点: " + found);
}/*** 过滤树节点*/
public void filterTree() {List<Menu> menuList = getMenuListFromDB();List<Tree<String>> trees = TreeUtil.build(menuList, "0");// 只保留包含"管理"的节点List<Tree<String>> filtered = TreeUtil.filter(trees,node -> node.getName().contains("管理"));System.out.println("过滤后的树: " + JSONUtil.toJsonPrettyStr(filtered));
}

}

Hutool TreeUtil 的优势

  1. 简单易用:几行代码即可完成复杂树形构建

  2. 功能丰富:支持排序、过滤、查找等多种操作

  3. 灵活配置:支持自定义节点映射和排序规则

  4. 性能优秀:底层使用高效的算法实现

  5. 零依赖:Hutool 的其他模块已经包含了所需功能

总结

使用 Hutool 的 TreeUtil 可以大大简化树形结构的构建工作:

// 最简单的用法
List<Tree> tree = TreeUtil.build(menuList, "0");

// 如果需要排序,确保实体类实现了 Comparable 接口或使用 weight 字段

强烈推荐使用 Hutool 的 TreeUtil,它比手动实现更简洁、更可靠、功能更强大!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/982744.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI生成代码系列:在不干扰开发者体验的情况下集成开源代码片段检测

AI生成代码系列:在不干扰开发者体验的情况下集成开源代码片段检测转载自FossID AI生成代码:如何快速推进且不破坏现有系统 在生成式人工智能Gen-AI时代,企业应对软件风险管理的方式正发生实质性转变。软件工程团队正…

2025 武汉文化课集训学校权威推荐榜单

随着高考竞争的日益激烈,优质的文化课集训学校成为高三学子提升成绩的重要助力。本榜单基于武汉市教育主管部门资质审核结果、第三方教育评估机构数据及学员真实反馈,从办学资质、师资力量、教学成果、管理体系等维度…

2025年国标mpp电力管直销厂家权威推荐榜单:mpp电力管/mpp电缆保护管/mpp电力管穿线管源头厂家精选

在城市化进程持续加速与电网升级改造的背景下,MPP电力管作为电力电缆保护的核心材料,其质量直接关系到电网工程的安全性与使用寿命。严格的国标产品与规模化源头厂家,已成为市政工程、电网建设等领域采购决策的关键…

2025年密封垫片生产厂家联系方式完整汇总:全国重点企业官方联系方式与高效采购指引

本文基于2025年行业公开数据和中国摩擦密封材料协会报告,结合推荐对象参考内容,从专业能力、服务稳定性、资源网络等维度筛选5个推荐对象,旨在为密封垫片生产厂家领域提供可靠联系方案。根据中国密封材料行业年度报…

标题:2025年密封垫片生产厂家联系方式完整汇总:全国重点企业官方联系方式与高效采购指引

本文基于2025年行业公开数据和权威第三方报告,结合推荐对象参考内容,从专业能力、服务稳定性、资源网络等维度筛选5个推荐对象,旨在为密封垫片生产厂家领域提供可靠联系方案。根据中国摩擦密封材料协会数据显示,20…

2025年评价高的多功能移动餐车十大热门品牌推荐榜

2025年评价高的多功能移动餐车热门品牌推荐榜 开篇:行业背景与市场趋势 随着城市化进程加快和消费升级,移动餐车行业在2025年迎来爆发式增长。这种灵活、低成本的餐饮经营模式不仅满足了消费者对便捷、多样化美食的…

波束赋形MATLAB代码实现

一、波束赋形基本原理 波束赋形(Beamforming)是一种通过控制天线阵列中各阵元的幅度和相位,在特定方向上增强信号强度、抑制干扰的空间滤波技术。核心原理是利用相控阵原理,通过调整各阵元信号的加权系数,使阵列在目…

2025年真空袋厂家联系电话完整汇总:全国重点产区企业联系方式及高效采购指引

本文基于2025年行业公开数据和权威第三方报告,结合推荐对象参考内容,从专业能力、服务稳定性、资源网络等维度筛选5个推荐对象,旨在为真空袋厂家联系电话领域提供可靠联系方案。根据中国包装联合会2024年行业报告显…

2025年三层别墅电梯优质厂家权威推荐榜单:曳引别墅电梯‌/液压别墅电梯‌/液压别墅电梯‌源头厂家精选

在高端住宅需求持续升级的2025年,一台量身定制的三层别墅电梯已成为品质生活的重要标配。据行业数据显示,2024年中国别墅电梯市场规模已达85亿元,同比增长18.3%,预计2025年将突破100亿元大关。 在消费升级与人口老…

2025年知名的南通家纺/设计感家纺高品质好评榜单

2025年知名的南通家纺/设计感家纺高品质好评榜单开篇:中国家纺行业现状与南通市场地位中国家纺行业近年来保持稳健增长态势,2023年市场规模已突破3000亿元,预计到2025年将实现年均6%以上的复合增长率。作为全球的家…

2025年聚氨酯发泡保温厂家联系电话完整汇总:全国重点企业和官方联系方式高效合作指引

本文基于2025年行业公开数据和权威第三方报告,结合推荐对象参考内容,从专业资质、技术实力、服务网络等维度筛选5个推荐对象,旨在为聚氨酯发泡保温领域提供可靠联系方案。根据中国建筑节能协会保温材料专委会数据显…

2025年真空袋厂家联系电话完整汇总:全国重点企业官方联系方式与高效采购指南

本文基于2025年行业公开数据和权威第三方报告,结合推荐对象参考内容,从专业能力、服务稳定性、资源网络等维度筛选5个推荐对象,旨在为真空袋领域提供可靠联系方案。根据中国包装联合会2024年行业报告显示,我国真空…

2025年线上1对1记单词课程电话联系方式汇总:全国主流品牌官方联系方式与高效选课指引

本文基于2025年行业公开数据及教育部教育信息化白皮书,结合推荐对象参考内容,从技术实力、服务模式、用户反馈等维度筛选5个推荐对象,旨在为线上1对1记单词课程领域提供可靠联系方案。根据中国教育学会2024年调研数…

2025年聚氨酯发泡保温厂家联系电话完整汇总:全国重点企业官方联系方式与高效合作指引

本文基于2025年行业公开数据和权威第三方报告,结合推荐对象参考内容,从专业资质、服务能力、产品性能等维度筛选5个推荐对象,旨在为聚氨酯发泡保温领域提供可靠联系方案。根据中国建筑材料联合会2024年发布的行业报…

2025年知名的卷制轴承/复合轴承行业内口碑厂家排行榜

2025年知名的卷制轴承/复合轴承行业内口碑厂家排行榜行业背景与市场趋势随着工业4.0和智能制造的深入推进,卷制轴承和复合轴承作为机械传动系统中的关键部件,正迎来新一轮的发展机遇。2023-2025年全球轴承市场规模预…

2025年湖南省长沙打井管厂家权威推荐榜单:打井管生产厂家联系方式‌/打井管厂家有哪些‌/打井管厂家‌源头厂家精选

在深地工程与能源开发持续推进的背景下,湖南打井管产业凭借技术突破与制造实力,已成为全国能源管材供应链的重要一环。 在油气开采与水资源开发需求日益增长的2025年,打井管作为钻井工程的关键材料,其技术含量与质…

2025年江苏钢球厂家哪家好?十大钢球制造企业价格合理排行榜

为帮企业高效锁定适配自身需求的钢球合作伙伴,避免选型走弯路,我们从生产技术实力(如原材料纯度控制、精度检测体系)、产品质量稳定性(含批次一致性、耐磨寿命测试)、价格性价比(覆盖成本管控、定制化报价透明度…

Asp.net 后端Api和SPA(单页面应用)整合

Asp.net 后端Api和SPA(单页面应用)整合builder.Services.AddSpaStaticFiles(op => op.RootPath = "ClientApp");app.MapWhen(ctx => !ctx.Request.Path.StartsWithSegments("/api"), appB…

2025国内战略咨询公司哪家好?优质品牌咨询公司推荐

2025国内战略咨询公司哪家好?优质品牌咨询公司推荐!在当前的经济环境中,企业面临的挑战日益复杂。战略咨询的价值,在于为企业提供清晰的发展方向和可行的落地路径。这个行业正朝着更加注重实效、深度陪伴和行业专精…

2025年12月北京陪诊公司推荐榜:专业机构对比与选择指南

在医疗资源高度集中的北京,许多患者及家属面临着就医流程复杂、时间紧张、信息不对称等实际困难。无论是外地来京就医需要系统引导,还是本地居民希望提升就诊效率,专业陪诊服务逐渐成为缓解这些痛点的有效选择。当前…