news 2026/6/12 20:33:09

手把手教你封装一个树形结构处理类(Java 通用 Tree 工具,支持无限层级)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你封装一个树形结构处理类(Java 通用 Tree 工具,支持无限层级)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!


🧩 一、需求场景:为什么需要树形结构?

在实际开发中,树形结构无处不在:

  • 组织架构:公司 → 部门 → 小组 → 员工;
  • 菜单权限:系统管理 → 用户管理 → 新增/编辑;
  • 商品分类:电子产品 → 手机 → 智能手机 → 国产;
  • 评论回复:主评论 → 子评论 → 孙评论。

但数据库通常用“平铺表”存储(id,parent_id,name),如何高效转成树?

✅ 目标:一行代码,将 List 转为 Tree!


🛠️ 二、通用树节点接口设计

先定义一个通用接口,让所有树节点实现它:

import java.util.List; public interface TreeNode<T> { String getId(); String getParentId(); void setChildren(List<T> children); }

💡 使用泛型T,支持任意实体类(Menu、Dept、Category 等)。


📦 三、示例实体类(以菜单为例)

import lombok.Data; import java.util.ArrayList; import java.util.List; @Data public class Menu implements TreeNode<Menu> { private String id; private String parentId; private String name; private Integer sort; private List<Menu> children = new ArrayList<>(); @Override public String getId() { return this.id; } @Override public String getParentId() { return this.parentId; } @Override public void setChildren(List<Menu> children) { this.children = children; } }

🔔 注意:children字段必须存在,并提供 setter。


🌲 四、核心工具类:TreeBuilder(重点!)

import java.util.*; import java.util.stream.Collectors; public class TreeBuilder { /** * 将平铺列表构建成树形结构 * * @param nodes 所有节点(平铺) * @param rootParentId 根节点的 parentId(如 "0" 或 null) * @param <T> 节点类型 * @return 树形列表 */ public static <T extends TreeNode<T>> List<T> buildTree(List<T> nodes, String rootParentId) { if (nodes == null || nodes.isEmpty()) { return Collections.emptyList(); } // 1. 创建 id -> node 的映射,提升查找效率 O(1) Map<String, T> nodeMap = nodes.stream() .collect(Collectors.toMap(TreeNode::getId, node -> node)); // 2. 初始化 children 列表 Map<String, List<T>> childrenMap = new HashMap<>(); for (T node : nodes) { childrenMap.put(node.getId(), new ArrayList<>()); } // 3. 遍历所有节点,构建父子关系 List<T> roots = new ArrayList<>(); for (T node : nodes) { String parentId = node.getParentId(); if (Objects.equals(parentId, rootParentId)) { // 是根节点 roots.add(node); } else { // 找到父节点,添加到其 children T parent = nodeMap.get(parentId); if (parent != null) { childrenMap.get(parentId).add(node); } // 如果父节点不存在,可选择忽略或抛异常 } } // 4. 设置每个节点的 children for (Map.Entry<String, List<T>> entry : childrenMap.entrySet()) { String nodeId = entry.getKey(); T node = nodeMap.get(nodeId); if (node != null) { node.setChildren(entry.getValue()); } } return roots; } }

✅ 时间复杂度:O(n),远优于递归(O(n²))!


🧪 五、使用示例

1. 模拟数据库数据

public class TreeDemo { public static void main(String[] args) { List<Menu> menus = Arrays.asList( new Menu("1", "0", "系统管理", 1), new Menu("2", "0", "内容管理", 2), new Menu("3", "1", "用户管理", 1), new Menu("4", "1", "角色管理", 2), new Menu("5", "3", "新增用户", 1), new Menu("6", "3", "编辑用户", 2), new Menu("7", "2", "文章管理", 1) ); // 构建树 List<Menu> tree = TreeBuilder.buildTree(menus, "0"); // 打印结果(可用 Jackson 格式化) System.out.println(tree); } }

2. 输出效果(结构化)

[ { "id": "1", "parentId": "0", "name": "系统管理", "children": [ { "id": "3", "parentId": "1", "name": "用户管理", "children": [ {"id": "5", "parentId": "3", "name": "新增用户", "children": []}, {"id": "6", "parentId": "3", "name": "编辑用户", "children": []} ] }, { "id": "4", "parentId": "1", "name": "角色管理", "children": [] } ] }, { "id": "2", "parentId": "0", "name": "内容管理", "children": [ {"id": "7", "parentId": "2", "name": "文章管理", "children": []} ] } ]

✅ 完美支持无限层级


❌ 六、反例 & 常见错误

反例 1:用递归构建树(性能差)

// ❌ 每找一个子节点都要遍历整个 list,n 层树 → O(n²) public List<Menu> buildByRecursion(String parentId, List<Menu> all) { return all.stream() .filter(m -> Objects.equals(m.getParentId(), parentId)) .peek(m -> m.setChildren(buildByRecursion(m.getId(), all))) .collect(Collectors.toList()); }

💥 数据量大时(如 1000+ 节点),响应慢到超时!


反例 2:不处理“父节点不存在”的情况

  • 数据库里有个节点parentId = "999",但id=999的记录被删了;
  • 如果不处理,可能 NPE 或数据丢失。

✅ 建议:

  • 日志告警:“孤立节点:id=xxx”;
  • 或自动挂到根节点下(根据业务决定)。

反例 3:硬编码实体类,无法复用

// ❌ 只能处理 Menu,不能处理 Dept、Category public class MenuTreeBuilder { ... }

✅ 正确:用泛型 + 接口,一套代码通吃所有树!


⚠️ 七、增强建议(生产级)

需求实现方式
排序buildTree后对children调用sort()
过滤支持传入Predicate<T>过滤节点
路径递归生成path = /系统管理/用户管理
扁平化提供flattenTree()方法反向操作
循环引用检测构建时检查id == parentId或环形依赖

例如,加排序:

// 在 buildTree 最后加: roots.forEach(TreeBuilder::sortChildren); private static <T extends TreeNode<T>> void sortChildren(T node) { if (node instanceof Comparable) { node.getChildren().sort(null); } node.getChildren().forEach(TreeBuilder::sortChildren); }

🎯 八、总结

特性说明
通用性任何实现TreeNode的类都能用
高性能O(n) 时间复杂度,Map 索引加速
安全处理孤立节点、空值等边界情况
易用一行代码TreeBuilder.buildTree(list, "0")

从此告别手写递归,轻松搞定组织架构、菜单、分类树!


视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/31 19:48:38

【26美赛C题】2026美赛数学建模(MCM/ICM)思路解析及代码分享

【26美赛C题】2026美赛数学建模赛&#xff08;MCM/ICM&#xff09;思路解析及代码分享 订阅即可获取2026年及历年数学建模笔记&#xff0c;万字题解内容&#xff0c;且结合全球最新AI技术辅助&#xff0c;帮你轻松攻坚竞赛&#xff01;后续还将持续发布华为杯、高教社杯、华数杯…

作者头像 李华
网站建设 2026/6/5 20:27:44

BurpSuite入门及详细使用教程(内附学习笔记)

Burp Suite是用于攻击web应用程序的集成平台&#xff0c;接下来通过本文给大家介绍Burpsuite入门及使用详细教程&#xff0c;感兴趣的朋友一起看看吧。&#xff08;片尾有惊喜哦~&#xff09; 1、简介 Burp Suite是用于攻击web应用程序的集成平台。它包含了许多工具&#xff…

作者头像 李华
网站建设 2026/6/11 13:25:48

为什么说运维工程师做不长久,做两年就赶快转网络安全或者研发

很多从事IT网络运维工作的年轻小伙伴都会有个疑问&#xff0c;自己做的工作很杂似乎很基础&#xff0c;而且重复很多年&#xff0c;究竟有没前途。 作为过来人告诉一个总结&#xff1a;前途大小&#xff0c;工资多少跟你的岗位和职称资质没有多少关系&#xff0c;跟你的经验技能…

作者头像 李华
网站建设 2026/6/10 15:02:10

云平台安全风险聚焦:谷歌云生态正成为网络攻击的“高风险区”

CyCognito最新研究报告显示&#xff0c;在云服务商安全状况对比中&#xff0c;谷歌云及部分中小云平台存在漏洞的资产比例显著高于亚马逊云服务&#xff08;AWS&#xff09;和微软Azure。这项基于近500万个互联网暴露资产的研究发现&#xff0c;随着多云环境复杂性提升和在线资…

作者头像 李华
网站建设 2026/6/10 17:15:40

74页可编辑PPT | 数据架构设计总体规划方案

很多公司做报表时&#xff0c;同一个客户名字在不同系统里写法不同。数据拿不准&#xff0c;领导不敢用。系统越建越多&#xff0c;数据却越存越乱。主数据没人统一&#xff0c;口径对不上&#xff0c;分析结果相差大。数据质量没人考核&#xff0c;错误反复出现。 方案介绍这…

作者头像 李华
网站建设 2026/6/11 8:24:33

如何用Notion管理AI测试项目?2026年模板

AI测试管理的变革与Notion的核心价值 在2026年&#xff0c;AI测试已成为软件开发生命周期的关键环节&#xff0c;但传统工具难以应对动态需求。Notion作为集成数据库、文档和AI的协作平台&#xff0c;解决了测试管理的核心痛点&#xff1a;用例版本混乱导致回归测试失误&#…

作者头像 李华