首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 通用樹構建器:從扁平列表到層級結構的最佳實踐

     編輯:Java 綜合教學

一個接口搞定所有樹形業務:部門、菜單、分類...

業務系統中部門架構、菜單導航、商品分類、地區劃分本質都是樹形結構。它們共享同一組核心屬性:id、name、parentId。本文提供一套零侵入、泛型通用的樹構建方案:定義接口 + 工具類遞歸構建,一次編寫,到處復用。

一、核心接口:TreeObject

public interface TreeObject {
    Object getId(); void setId(Object id);
    Object getParentId(); void setParentId(Object parentId);
    String getName(); void setName(String name);
    List getChildren(); void setChildren(List children);
}

設計要點:

  • 使用 Object 類型支援 Long/String/Integer 等多種 ID
  • 僅定義構建樹所需的最小屬性集,業務字段由實現類自行擴展
  • 不強制繼承基類,接口方式對現有實體零侵入

二、構建引擎:TreeUtil 核心邏輯

提供三種構建入口,覆蓋不同業務場景:

1. 指定根節點構建 getChildTreeObjects(list, parentId)

  • 適用:已知父 ID(如「展開某部門下級」)
  • 邏輯:遍歷列表 → 匹配 parentId → 遞歸掛載子節點

2. 自動識別根節點 getChildTreeObjects(list) ⭐ 推薦

public List<TreeObject> getChildTreeObjects(List<TreeObject> list) {
    // 1. 分離頂層節點(parentId 為 null/空)與子節點
    // 2. 建立 ID→對象映射表,O(1) 查找父節點是否存在
    // 3. 將孤兒節點(父ID不在列表中)提升為頂層節點
    // 4. 遍歷頂層節點,遞歸掛載子節點
}

健壯性亮點:自動處理「髒數據」(父節點缺失、頂層節點非空等邊界情況)。

3. 帶前綴輸出 getChildTreeObjects(list, parentId, prefix)

  • 適用:控制台調試、日志輸出樹狀縮進
  • 遞歸時疊加前綴字符串(如 "│ " → "│ │ ")

三、實戰:菜單樹構建完整示例

1. 實體實現接口

@ApiModel("菜單樹對象")
public class MenuTreeDto implements Serializable, TreeObject {
    @ApiModelProperty("菜單ID") private String id;
    @ApiModelProperty("菜單名稱") private String menuName;
    @ApiModelProperty("父節點ID") private String parentId;
    @ApiModelProperty("子菜單") private List<MenuTreeDto> children;
    // ... 其他業務字段:menuCode, permission, iconcls, sortNo 等

    // 接口方法實現(略)
    @Override public Object getId() { return id; }
    @Override public void setId(Object id) { this.id = (String) id; }
    // ...
}

2. Service 層調用

List<MenuTreeDto> dtoList = BeanMapper.mapList(menuList, MenuTreeDto.class);
if (CollectionUtils.isNotEmpty(dtoList)) {
    TreeUtil treeUtil = new TreeUtil();
    // 方案 A:指定父ID(如 parentId="0" 為頂級)
    List<MenuTreeDto> treeList = treeUtil.getChildTreeObjects(dtoList, "0");
    // 方案 B:自動識別根節點(無需傳 parentId)
    // List<MenuTreeDto> treeList = treeUtil.getChildTreeObjects(dtoList);
    return treeList;
}

四、輸出效果:標準 JSON 樹結構

菜單樹 JSON 結構示例

{
  "success": true,
  "data": [
    {
      "id": "1", "menuName": "系統管理", "parentId": "0",
      "children": [
        {"id": "2", "menuName": "用戶管理", "parentId": "1", "children": null},
        {"id": "3", "menuName": "部門管理", "parentId": "1", "children": null}
      ]
    },
    {
      "id": "9", "menuName": "調度中心", "parentId": "0",
      "children": [...]
    }
  ]
}

五、性能與擴展建議

六、原理圖解:遞歸掛載流程

列表 [A(p=0), B(p=A), C(p=A), D(p=B), E(p=0)]
        ↓ 分離頂層/子層
頂層 [A, E]     子層 [B, C, D]
        ↓ 遞歸掛載
A.children → [B, C]  (匹配 parentId=A)
B.children → [D]     (匹配 parentId=B)
E.children → []
        ↓ 完成
樹根 [A, E]

「把複雜的遞歸封裝起來,業務代碼只需關注『什麼數據』,不關心『怎麼構建』。」
這就是通用工具類的價值所在。

完整代碼已集成至項目公共模塊,歡迎直接引用 com.mjwon.core.tree.*。下期預告:數據庫層面的樹模型設計(遞歸CTE vs Closure Table)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景優化方案
數據量 > 10 萬改用 Map<Id, Node> + 單次遍歷構建,時間複雜度 O(N)
頻繁讀寫引入 Nested Set 或 Closure Table 數據庫模型
前端樹組件直接返回此 JSON,配合 Element UI / Ant Design Tree 組件零適配
權限過濾構建前先按權限過濾列表,再構建樹