欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java構(gòu)建樹形結(jié)構(gòu)的方式及如何組裝樹狀結(jié)構(gòu)數(shù)據(jù)

 更新時(shí)間:2025年04月17日 08:28:36   作者:小學(xué)雞!  
這篇文章主要介紹了在Java中構(gòu)建樹狀數(shù)據(jù)結(jié)構(gòu)的幾種常見方法,包括遞歸、使用Map/HashMap以及基于Stream流的方式,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

思考思路

我在考慮如何在Java中構(gòu)建樹狀數(shù)據(jù)結(jié)構(gòu):常見的方法包括遞歸、父子引用和使用節(jié)點(diǎn)列表等??梢酝ㄟ^創(chuàng)建樹節(jié)點(diǎn)類來表示樹形結(jié)構(gòu),例如,可以定義“樹節(jié)點(diǎn)”類為:class Node { T data; List<Node> children; Node parent; }

我將提供詳細(xì)分析,看看哪些方法在實(shí)際項(xiàng)目中更常見,如何構(gòu)建包含父子關(guān)系和操作樹的數(shù)據(jù)結(jié)構(gòu)。

這個(gè)問題的答案可能會(huì)包括多個(gè)常見的實(shí)現(xiàn)方式。

在 Java 中構(gòu)建樹狀結(jié)構(gòu),常見方法包括:1. 遞歸:從父子關(guān)系數(shù)據(jù)遞歸構(gòu)建樹;2. 使用 Map/HashMap:存儲(chǔ)節(jié)點(diǎn)并根據(jù)父子關(guān)系鏈接它們。我可以提供代碼示例,如定義TreeNode 類如下:

public class TreeNode<T> {
    private T data;
    private List<TreeNode<T>> children = new ArrayList<>();
    public TreeNode(T data) {
        this.data = data;
    }
    public void addChild(TreeNode<T> child) {
         children.add(child);
    }
}

實(shí)現(xiàn):Java構(gòu)建樹狀結(jié)構(gòu)數(shù)據(jù)

目的: 在 Java 中構(gòu)建樹狀結(jié)構(gòu)數(shù)據(jù)是一個(gè)非常常見的需求,通常用于展示分層數(shù)據(jù)(如:組織架構(gòu)、菜單、文件目錄等),下面詳細(xì)介紹幾種常見的方法、代碼案例及實(shí)際項(xiàng)目中的實(shí)踐經(jīng)驗(yàn)。比如實(shí)現(xiàn)這樣的分層效果:

這些數(shù)據(jù)都在數(shù)據(jù)庫,所以查數(shù)據(jù)庫:查出所有的數(shù)據(jù),然后再組裝返回給前端解析即可,當(dāng)然數(shù)據(jù)表中應(yīng)該設(shè)計(jì)成這樣:

在這里插入圖片描述

組裝時(shí)只需要組裝成一個(gè) list 返回即可,前端拿到 list 解析。

1. 遞歸方式

原理: 遞歸方法基于“父子關(guān)系”的層級(jí)遍歷,從根節(jié)點(diǎn)開始遞歸查找和添加子節(jié)點(diǎn)。適合節(jié)點(diǎn)層次較少的場(chǎng)景。

實(shí)現(xiàn): 利用遞歸的方式進(jìn)行樹狀結(jié)構(gòu)數(shù)據(jù)的組裝,代碼如下:

@Data
@NoArgsConstructor
public class DeptEntity {
    private Long deptId;
    private Long parentId;
    private String deptName;
    private List<DeptEntity> children;

    public DeptEntity(Long deptId, Long parentId, String deptName) {
        this.deptId = deptId;
        this.parentId = parentId;
        this.deptName = deptName;
    }
}
public class Test {
    // 假數(shù)據(jù):本來這一步是需要從數(shù)據(jù)庫中查找所有的數(shù)據(jù),然后再組裝,為了方便,這里弄成假數(shù)據(jù)
    public static List<DeptEntity> getData() {
        List<DeptEntity> list = new ArrayList<>();
        list.add(new DeptEntity(100L, 0L, "若依科技"));
        list.add(new DeptEntity(101L, 100L, "深圳總公司"));
        list.add(new DeptEntity(102L, 100L, "長(zhǎng)沙分公司"));
        list.add(new DeptEntity(103L, 101L, "研發(fā)部門"));
        list.add(new DeptEntity(104L, 101L, "市場(chǎng)部門"));
        list.add(new DeptEntity(105L, 102L, "運(yùn)維部門"));
        list.add(new DeptEntity(106L, 102L, "財(cái)務(wù)部門"));
        return list;
    }

    public static void main(String[] args) {
        recursionBuildTree();
    }

    // 方法1:遞歸
    public static void recursionBuildTree() {
        // 獲取假數(shù)據(jù)
        List<DeptEntity> data = getData();
        // 留住最頂層節(jié)點(diǎn)
        List<DeptEntity> collect = data.stream()
                .filter(d -> d.getParentId() == 0L)
                .collect(Collectors.toList());
        //首先設(shè)置最頂層子節(jié)點(diǎn)
        for (DeptEntity deptEntity : collect) {
            deptEntity.setChildren(recursionBuildChildren(deptEntity.getDeptId(), data));
        }
        System.out.println(collect);
    }

    // 設(shè)置子節(jié)點(diǎn)
    public static List<DeptEntity> recursionBuildChildren(Long deptId, List<DeptEntity> data) {
        // 保存子節(jié)點(diǎn)
        List<DeptEntity> children = new ArrayList<>();
        // 尋找子節(jié)點(diǎn)
        for (DeptEntity deptEntity : data) {
            // 因?yàn)閐ata是最原始數(shù)據(jù),還是需要過濾掉最頂層節(jié)點(diǎn)
            if (deptEntity.getParentId() == 0L) {
                continue;
            }
            if (deptEntity.getParentId().equals(deptId)) {
                children.add(deptEntity);
            }
        }
        //遞歸遍歷子節(jié)點(diǎn)
        for (DeptEntity deptEntity : children) {
            deptEntity.setChildren(recursionBuildChildren(deptEntity.getDeptId(), data));
        }
        return children;
    }
}

2. 利用Stream流

1、上面的 DeptEntity 類代碼不變

2、寫方法:

public class Test {
    // 假數(shù)據(jù):本來這一步是需要從數(shù)據(jù)庫中查找所有的數(shù)據(jù),然后再組裝,為了方便,這里弄成假數(shù)據(jù)
    public static List<DeptEntity> getData() {
        List<DeptEntity> list = new ArrayList<>();
        list.add(new DeptEntity(100L, 0L, "若依科技"));
        list.add(new DeptEntity(101L, 100L, "深圳總公司"));
        list.add(new DeptEntity(102L, 100L, "長(zhǎng)沙分公司"));
        list.add(new DeptEntity(103L, 101L, "研發(fā)部門"));
        list.add(new DeptEntity(104L, 101L, "市場(chǎng)部門"));
        list.add(new DeptEntity(105L, 102L, "運(yùn)維部門"));
        list.add(new DeptEntity(106L, 102L, "財(cái)務(wù)部門"));
        return list;
    }

    public static void main(String[] args) {
       streamBuildTree();
    }

    // 方法2:Stream流
    public static void streamBuildTree() {
        List<DeptEntity> data = getData();
        // 按照parentId分組,將數(shù)據(jù)轉(zhuǎn)為Map<parentId, 屬于這個(gè)parentId的數(shù)據(jù)的一個(gè)集合>
        Map<Long, List<DeptEntity>> collect = data.stream()
                .collect(Collectors.groupingBy(DeptEntity::getParentId));

        // 開始構(gòu)建樹
        List<DeptEntity> list = streamBuildChildren(0L, collect);
        System.out.println(list);
    }

    // 設(shè)置子節(jié)點(diǎn)
    public static List<DeptEntity> streamBuildChildren(Long parentId, Map<Long, List<DeptEntity>> collect) {
        return Optional.ofNullable(collect.get(parentId))
                .orElse(new ArrayList<>()) // 避免報(bào)空指針異常
                .stream()
                .peek(child -> child.setChildren(streamBuildChildren(child.getDeptId(), collect)))
                .collect(Collectors.toList());
    }

}

3. 基于Map(推薦)

原理: 首先將所有節(jié)點(diǎn)存入一個(gè)哈希表(Map),鍵為節(jié)點(diǎn)的 id。然后遍歷所有節(jié)點(diǎn),根據(jù)其 parentId 找到對(duì)應(yīng)父節(jié)點(diǎn)并加入到父節(jié)點(diǎn)的子節(jié)點(diǎn)列表中。這樣可以避免遞歸的性能問題。

好處: 在 Java 中,使用 Map 來構(gòu)建樹結(jié)構(gòu)是一種高效的方法。該方法避免了遞歸的性能問題,并且可以快速查找節(jié)點(diǎn),提高構(gòu)建樹的效率。

實(shí)現(xiàn): 如下:

1、上面的 DeptEntity 類代碼不變

2、寫方法:

public class Test {
    // 假數(shù)據(jù):本來這一步是需要從數(shù)據(jù)庫中查找所有的數(shù)據(jù),然后再組裝,為了方便,這里弄成假數(shù)據(jù)
    public static List<DeptEntity> getData() {
        List<DeptEntity> list = new ArrayList<>();
        list.add(new DeptEntity(100L, 0L, "若依科技"));
        list.add(new DeptEntity(101L, 100L, "深圳總公司"));
        list.add(new DeptEntity(102L, 100L, "長(zhǎng)沙分公司"));
        list.add(new DeptEntity(103L, 101L, "研發(fā)部門"));
        list.add(new DeptEntity(104L, 101L, "市場(chǎng)部門"));
        list.add(new DeptEntity(105L, 102L, "運(yùn)維部門"));
        list.add(new DeptEntity(106L, 102L, "財(cái)務(wù)部門"));
        return list;
    }

    public static void main(String[] args) {
        mapBuildTree();
    }

    // 方法3:基于Map 1
    public static void mapBuildTree() {
        // 獲取假數(shù)據(jù)
        List<DeptEntity> data = getData();

        // 存放根節(jié)點(diǎn)
        List<DeptEntity> roots = new ArrayList<>();
        // 存放所有子節(jié)點(diǎn)
        Map<Long, List<DeptEntity>> childMap = new HashMap<>();

        // 1.初始化childMap,確保不會(huì)出現(xiàn)null
        for (DeptEntity deptEntity : data) {
            childMap.put(deptEntity.getDeptId(), new ArrayList<>());
        }
        // 2.構(gòu)建樹結(jié)構(gòu)
        for (DeptEntity deptEntity : data) {
            // 根節(jié)點(diǎn)
            if (deptEntity.getParentId() == null || deptEntity.getParentId() == 0) {
                roots.add(deptEntity);
            } else {
                // 非根節(jié)點(diǎn),加入到父節(jié)點(diǎn)的children列表
                childMap.get(deptEntity.getParentId()).add(deptEntity);
            }
        }
        // 3.統(tǒng)一賦值children
        for (DeptEntity deptEntity : data) {
            deptEntity.setChildren(childMap.get(deptEntity.getDeptId()));
        }

        System.out.println(roots);

    }
}

基于 Map 的方式還有一個(gè),如下:

public class DeptEntity {
    private Long deptId;
    private Long parentId;
    private String deptName;
    private List<DeptEntity> children = new ArrayList<>();

    // 構(gòu)造方法
    public DeptEntity(Long deptId, Long parentId, String deptName) {
        this.deptId = deptId;
        this.parentId = parentId;
        this.deptName = deptName;
    }

    // Getter & Setter
    public Long getDeptId() { return deptId; }
    public Long getParentId() { return parentId; }
    public String getDeptName() { return deptName; }
    public List<DeptEntity> getChildren() { return children; }
    
    // 加一個(gè)這個(gè)方法
    public void addChild(DeptEntity child) {
        this.children.add(child);
    }
}
public class Test {
    // 假數(shù)據(jù):本來這一步是需要從數(shù)據(jù)庫中查找所有的數(shù)據(jù),然后再組裝,為了方便,這里弄成假數(shù)據(jù)
    public static List<DeptEntity> getData() {
        List<DeptEntity> list = new ArrayList<>();
        list.add(new DeptEntity(100L, 0L, "若依科技"));
        list.add(new DeptEntity(101L, 100L, "深圳總公司"));
        list.add(new DeptEntity(102L, 100L, "長(zhǎng)沙分公司"));
        list.add(new DeptEntity(103L, 101L, "研發(fā)部門"));
        list.add(new DeptEntity(104L, 101L, "市場(chǎng)部門"));
        list.add(new DeptEntity(105L, 102L, "運(yùn)維部門"));
        list.add(new DeptEntity(106L, 102L, "財(cái)務(wù)部門"));
        return list;
    }

    public static void main(String[] args) {
        mapBuildTree();
    }

    // 方法3:基于Map 2
    public static void mapBuildTree() {
        // 獲取假數(shù)據(jù)
        List<DeptEntity> data = getData();

        List<DeptEntity> roots = new ArrayList<>();
        Map<Long, DeptEntity> childMap = new HashMap<>();

        // 1.將所有節(jié)點(diǎn)存入Map中
        for (DeptEntity deptEntity : data) {
            childMap.put(deptEntity.getDeptId(), deptEntity);
        }
        // 2.構(gòu)建樹結(jié)構(gòu)
        for (DeptEntity deptEntity : data) {
            // 根節(jié)點(diǎn)
            if (deptEntity.getParentId() == null || deptEntity.getParentId() == 0) {
                roots.add(deptEntity);
            } else {
                // 非根節(jié)點(diǎn),加入到父節(jié)點(diǎn)的children列表
                DeptEntity parent = map.get(deptEntity.getParentId());
                if (parent != null) {
                    parent.addChild(deptEntity);
                }
            }
        }
        
        System.out.println(roots);

    }
}

3.1 基于Map方式分析

1、代碼分析:

  • 先將所有節(jié)點(diǎn)存入 Map<Long, DeptEntity>,便于快速查找。
  • 遍歷 data:
    1、如果 parentId 為 null 或 0,表示它是根節(jié)點(diǎn),加入 roots 列表。
    2、否則,在 map 中查找 parentId 對(duì)應(yīng)的父節(jié)點(diǎn),并將該節(jié)點(diǎn)加入父節(jié)點(diǎn)的 children 列表。

2、優(yōu)勢(shì)分析:
(1)時(shí)間復(fù)雜度:遍歷列表 data 兩次,因此時(shí)間復(fù)雜度為 O(n),相比遞歸構(gòu)建樹(可能達(dá)到 O(n²))效率更高。

(2)空間復(fù)雜度:使用了 Map<Long, DeptEntity>,其空間復(fù)雜度是 O(n),額外開銷較小。

(3)避免遞歸問題:采用 Map 直接查找父節(jié)點(diǎn),避免遞歸調(diào)用,避免棧溢出問題,特別適用于深度較大的樹結(jié)構(gòu)。

3、適合場(chǎng)景:

? 數(shù)據(jù)量大,如百萬級(jí)別的分類、菜單、組織架構(gòu)等。

? 層級(jí)較深,如文件目錄、權(quán)限管理系統(tǒng)。

? 頻繁查詢,構(gòu)建后可以緩存樹,提高性能。

4、不適合場(chǎng)景:

? 數(shù)據(jù)量特別?。?lt;100),可以直接使用遞歸方法,代碼更簡(jiǎn)潔。

4. 總結(jié)

到此這篇關(guān)于java構(gòu)建樹形結(jié)構(gòu)的方式及如何組裝樹狀結(jié)構(gòu)數(shù)據(jù)的文章就介紹到這了,更多相關(guān)java構(gòu)建樹形結(jié)構(gòu)數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring MVC接口防數(shù)據(jù)篡改和重復(fù)提交

    Spring MVC接口防數(shù)據(jù)篡改和重復(fù)提交

    這篇文章主要為大家詳細(xì)介紹了Spring MVC接口防數(shù)據(jù)篡改和重復(fù)提交,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Springboot 1.5.7整合Kafka-client代碼示例

    Springboot 1.5.7整合Kafka-client代碼示例

    這篇文章主要介紹了Springboot 1.5.7整合Kafka-client代碼示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • spring boot 使用utf8mb4的操作

    spring boot 使用utf8mb4的操作

    這篇文章主要介紹了spring boot 使用utf8mb4的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 10個(gè)經(jīng)典的Java main方法面試題

    10個(gè)經(jīng)典的Java main方法面試題

    這篇文章主要為大家分享了10個(gè)經(jīng)典的Java main方法面試題,與其說是Java面試題,其實(shí)也是Java的一些最基礎(chǔ)知識(shí)問題,感興趣的小伙伴們可以參考一下
    2016-01-01
  • java代碼關(guān)閉tomcat程序及出現(xiàn)問題解析

    java代碼關(guān)閉tomcat程序及出現(xiàn)問題解析

    這篇文章主要介紹了java代碼關(guān)閉tomcat程序 及出現(xiàn)問題解析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2019-05-05
  • Java之Pattern.compile函數(shù)用法詳解

    Java之Pattern.compile函數(shù)用法詳解

    這篇文章主要介紹了Java之Pattern.compile函數(shù)用法詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java中類的加載順序剖析(常用于面試題)

    Java中類的加載順序剖析(常用于面試題)

    這篇文章主要介紹了Java中類的加載順序剖析(常用于面試題),本文直接給出代碼實(shí)例和運(yùn)行結(jié)果,給后給出了加載過程總結(jié),需要的朋友可以參考下
    2015-03-03
  • Springboot?HTTP如何調(diào)用其他服務(wù)

    Springboot?HTTP如何調(diào)用其他服務(wù)

    這篇文章主要介紹了Springboot?HTTP如何調(diào)用其他服務(wù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • IntelliJ?IDEA設(shè)置JVM運(yùn)行參數(shù)的圖文介紹

    IntelliJ?IDEA設(shè)置JVM運(yùn)行參數(shù)的圖文介紹

    這篇文章主要介紹了IntelliJ?IDEA設(shè)置JVM運(yùn)行參數(shù)的方法,包括配置方式及優(yōu)先級(jí),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • Dubbo?LoadBalance基于權(quán)重的隨機(jī)負(fù)載均衡算法提高服務(wù)性能

    Dubbo?LoadBalance基于權(quán)重的隨機(jī)負(fù)載均衡算法提高服務(wù)性能

    這篇文章主要為大家介紹了Dubbo?LoadBalance基于權(quán)重的隨機(jī)負(fù)載均衡算法提高服務(wù)性能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2023-10-10

最新評(píng)論