双重for循环组装商品分类树可行但需优化:先分离根节点,再遍历非根节点逐个匹配父节点挂载;高效关键在于避免重复遍历,推荐预建id→node映射表或按parent_id分组预处理。
直接用双重 for 循环组装商品分类树是可行的,但“高效”关键不在嵌套本身,而在如何避免重复遍历、减少无效比较,并让父子关系一次定位成功。
明确数据结构与目标层级
商品分类通常不是扁平列表,而是带 parent_id 的线性数据(如数据库查出的 List
- 假设 Category 类含 id、name、parent_id 字段,根节点 parent_id 为 null 或 0
- 目标结构是 List
roots,每个 Category 对象的 children 是 ArrayList - 不推荐在内层循环里反复调用 list.get(i).getParentId() == targetId 这类低效查找
用双重 for 实现基础组装(适合中小数据量)
核心逻辑:先收集所有根节点,再对每个非根节点,遍历已有节点找其父节点并 add 进 children。
- 第一轮:遍历原始列表,把 parent_id 为空的节点加入 roots
- 第二轮:对每个非根节点 node,遍历 roots 及其全部子孙(需递归展开或提前缓存所有节点映射)——但双重 for 不宜直接递归,所以改用“两层平铺比对”
- 更实用的做法:外层 for 遍历所有节点,内层 for 遍历当前已构建的完整节点集合(包括 roots 和已挂载的子节点),一旦 node.parent_id == candidate.id,就将 node 加入 candidate.children
必须配合的优化手段(否则效率骤降)
纯双重 for 在 1000+ 分类时可能达 O(n²),实际项目中必须叠加以下任一方式:
-
预建 ID → Node 映射表:单次遍历原始列表,用 HashMap
存所有节点。之后“找父节点”变成 O(1) 查 map.get(node.getParentId()),外层 for 即可直接挂载,根本不需要内层 for -
按 parent_id 分组预处理:用 Map
> groupByParent,键为 parent_id,值为所有子节点列表。再遍历一次,对每个 parent_id 对应的子节点批量挂载,避免逐个匹配 - 若坚持用双重 for,至少剪枝:内层循环只遍历当前已确认有 children 容器的节点(比如只扫 roots 列表 + 已挂过子节点的中间节点),而非每次扫全量
一个轻量但清晰的双重 for 实现片段(Java)
适用于分类数 ≤ 500,且不追求极致性能的管理后台:
ListList
// 第一步:找出所有根
for (Category c : all) {
if (c.getParentId() == null || c.getParentId() == 0) {
roots.add(c);
}
}
// 第二步:双重 for 挂载子节点
for (Category child : all) {
if (child.getParentId() == null || child.getParentId() == 0) continue;
// 外层是 child,内层扫整个当前树结构(roots 及其子孙)
attachToParent(child, roots); // 封装好的递归挂载方法,非纯 for;若坚持不用递归,则此处用 for 遍历 roots 并手动展开 children 层级
}
注意:这里第二步若真写成两层 for(外层 all,内层 flatten(roots)),flatten 成本高;所以实践中“双重 for”更多是思路,落地时会自然过渡到 map 查找或分组策略。










