stream 優(yōu)化遞歸

需求

有一個(gè)部門類,如下

public class Department {
    /**
     * 部門 id
     */
    private String id;

    /**
     * 部門名稱
     */
    private String name;

    /**
     * 上級(jí)部門id已慢, 頂級(jí)為 null
     */
    private String parentId;

    ... 省略 get set
}

有一組如下的數(shù)據(jù)需要進(jìn)行排序曲聂,排序規(guī)則為,1級(jí)部門后面是所有2級(jí)部門蛇受,如果2級(jí)部門存在下屬3級(jí)部門句葵,則該2級(jí)部門后緊跟著所有3級(jí)部門,以此類推兢仰。

List<Department> originalList = new ArrayList<>();
originalList.add(new Department("7", "國內(nèi)采購部", "5"));
originalList.add(new Department("8", "北上廣采購部", "7"));
originalList.add(new Department("9", "沿海采購部", "7"));
originalList.add(new Department("2", "國外采購部", "5"));
originalList.add(new Department("10", "歐美采購部", "2"));
originalList.add(new Department("11", "澳洲采購部", "2"));
originalList.add(new Department("3", "國內(nèi)銷售部", "6"));
originalList.add(new Department("4", "國外銷售部", "6"));
originalList.add(new Department("1", "運(yùn)營部", null));
originalList.add(new Department("5", "采購部", "1"));
originalList.add(new Department("6", "銷售部", "1"));

排序結(jié)果如下


排序結(jié)果.png

簡(jiǎn)單版

實(shí)現(xiàn)分析

  • 找到所有一級(jí)部門
  • 遍歷所有一級(jí)部門乍丈,找到是否存在二級(jí)部門刊懈,不存在返回
  • 遍歷所有二級(jí)部門庶橱,查找是否存在三級(jí)部門,不存在返回
  • 遍歷所有三級(jí)部門卵迂,查找是否存在四級(jí)部門察蹲,不存在返回
  • ...
    能看出來這是一個(gè)遞歸算法请垛,嘗試用偽代碼實(shí)現(xiàn)下邏輯
List<Department> 排序結(jié)果;

void 查找子部門(上級(jí)部門) {
  所有下級(jí)部門 = 查找所有下級(jí)部門()
  if (所有下級(jí)部門不存在) {
    return;
  }
  for (下級(jí)部門: 所有下級(jí)部門) {
    排序結(jié)果.添加(下級(jí)部門);
    查找子部門(下級(jí)部門);
  }
}

思路清楚了催训,就是找下級(jí),遍歷下級(jí)宗收,添加下級(jí)到結(jié)果集中漫拭,同時(shí)找下級(jí)的所有下級(jí)

代碼實(shí)現(xiàn)

public class DepartmentSort {

    private List<Department> deptList;

    private List<Department> resultList = new ArrayList<>();

    public DepartmentSort(List<Department> deptList) {
        this.deptList = deptList;
    }

    public static List<Department> sort(List<Department> originalList) {
        return new DepartmentSort(originalList).sort();
    }

    private List<Department> sort() {
        findChildren(new Department());
        return resultList;
    }

    /**
     * 查詢下級(jí)部門
     *
     * @param parentDept
     */
    private void findChildren(Department parentDept) {
        // 查找所有下級(jí)部門
        List<Department> childrenList = deptList.stream()
                .filter(d -> Objects.equals(parentDept.getId(), d.getParentId()))
                .collect(Collectors.toList());

        if (childrenList.isEmpty())
            /* 不存在下級(jí)部門,跳出遞歸 */
            return;
        else
            // 遍歷所有下級(jí)部門混稽,塞入下級(jí)部門 resultList 的同時(shí)采驻,查找所有該下級(jí)部門對(duì)應(yīng)的下級(jí)部門
            childrenList.forEach(d -> {
                resultList.add(d);
                findChildren(d);
            });
    }

}

編碼測(cè)試

List<Department> resultList = DepartmentSort.sort(originalList);

測(cè)試結(jié)果,達(dá)到了預(yù)期結(jié)果


測(cè)試結(jié)果1.png

優(yōu)化版

接下來匈勋,分析上個(gè)版本代碼的不足

  • 上個(gè)版本的實(shí)現(xiàn)思路是用 一個(gè) resultList 集合去存儲(chǔ)所有結(jié)果礼旅, findChildren 方法是返回 void 的
  • 但其實(shí)對(duì)于上級(jí)部門,調(diào)用 findChildren 方法洽洁,希望得到的是所有下級(jí)部門以及下級(jí)部門可能存在的所有更下一級(jí)部門的集合

帶著這幾條思路痘系,改造代碼如下

private List<Department> findChildren2(Department parentDept) {
    // 查找所有下級(jí)部門
    List<Department> childrenList = deptList.stream()
            .filter(d -> Objects.equals(parentDept.getId(), d.getParentId()))
            .collect(Collectors.toList());

    List<Department> tempList = new ArrayList<>();
    // 遍歷所有下級(jí)
    childrenList.forEach(d -> {
        tempList.add(d);
        tempList.addAll(findChildren2(d));
    });
    return tempList;
}

經(jīng)過測(cè)試,結(jié)果正確饿自,不貼圖了...

stream 優(yōu)化版

繼續(xù)分析上個(gè)版本代碼汰翠,依然存在不優(yōu)雅的地方

  • 需要弄一個(gè) tempList 存儲(chǔ)所有下級(jí)部門以及可能存在的所有更下一級(jí)部門
  • 雖然使用了 stream,但是只是用來過濾下級(jí)璃俗,大材小用了

利用 stream 流的思想去考慮問題奴璃,其實(shí)遍歷出來的下級(jí)和可能存在的所有下級(jí)部門的所有下級(jí)部門,是綁定在一起的城豁,也就是 tempList。根據(jù)這樣的關(guān)系抄课,很容易想到 map 操作符唱星,得到 下級(jí)部門.map(下級(jí)部門和所有下級(jí)部門的下級(jí)部門的集合)

帶著這個(gè)思路,實(shí)現(xiàn)如下代碼

public class DepartmentSort2 {

    private List<Department> deptList;

    public DepartmentSort2(List<Department> deptList) {
        this.deptList = deptList;
    }

    public static List<Department> sort(List<Department> originalList) {
        return new DepartmentSort2(originalList).sort();
    }

    private List<Department> sort() {
        return findChildren(new Department())
                .collect(Collectors.toList());
    }

    /**
     * 查詢下級(jí)部門
     *
     * @param parentDept
     */
    private Stream<Department> findChildren(Department parentDept) {
        return deptList.stream()
                .filter(d -> Objects.equals(parentDept.getId(), d.getParentId()))
                .flatMap(d -> Stream.concat(Stream.of(d), findChildren(d)));
    }

}

編寫測(cè)試代碼

List<Department> resultList2 = DepartmentSort2.sort(originalList);

測(cè)試結(jié)果


stream 版測(cè)試結(jié)果.png

源碼 https://github.com/MoonBottle/sortdemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末跟磨,一起剝皮案震驚了整個(gè)濱河市间聊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抵拘,老刑警劉巖哎榴,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異僵蛛,居然都是意外死亡尚蝌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門充尉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來飘言,“玉大人,你說我怎么就攤上這事驼侠∽撕瑁” “怎么了谆吴?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)苛预。 經(jīng)常有香客問我句狼,道長(zhǎng),這世上最難降的妖魔是什么热某? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任鲜锚,我火速辦了婚禮,結(jié)果婚禮上苫拍,老公的妹妹穿的比我還像新娘芜繁。我一直安慰自己,他們只是感情好绒极,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布骏令。 她就那樣靜靜地躺著,像睡著了一般垄提。 火紅的嫁衣襯著肌膚如雪榔袋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天铡俐,我揣著相機(jī)與錄音凰兑,去河邊找鬼。 笑死审丘,一個(gè)胖子當(dāng)著我的面吹牛吏够,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滩报,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锅知,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了脓钾?” 一聲冷哼從身側(cè)響起售睹,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎可训,沒想到半個(gè)月后昌妹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡握截,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年飞崖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片川蒙。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚜厉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畜眨,到底是詐尸還是另有隱情昼牛,我是刑警寧澤术瓮,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贰健,受9級(jí)特大地震影響胞四,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伶椿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一辜伟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脊另,春花似錦导狡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至踩麦,卻和暖如春枚赡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谓谦。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工贫橙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人反粥。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓卢肃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親星压。 傳聞我的和親對(duì)象是個(gè)殘疾皇子践剂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)娜膘,斷路器,智...
    卡卡羅2017閱讀 134,660評(píng)論 18 139
  • Kafka設(shè)計(jì)解析(七)- Kafka Stream 原創(chuàng)文章优质,轉(zhuǎn)載請(qǐng)務(wù)必將下面這段話置于文章開頭處竣贪。本文轉(zhuǎn)發(fā)自技...
    小小少年Boy閱讀 5,250評(píng)論 0 32
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法巩螃,內(nèi)部類的語法演怎,繼承相關(guān)的語法,異常的語法避乏,線程的語...
    子非魚_t_閱讀 31,639評(píng)論 18 399
  • 01.回娘家爷耀,我們都開心得像小鳥 昨天,博爸請(qǐng)了一天假拍皮,帶著我們一家人回我娘家歹叮。 因?yàn)樯毜木壒逝芎迹乙呀?jīng)有半年沒...
    小時(shí)光閣樓閱讀 737評(píng)論 0 1
  • 吃是中國人亙古不變的一個(gè)主題,我們每天都在吃咆耿,每天都會(huì)思考吃什么德谅,古代如此,現(xiàn)代更是喜愛萨螺。 吃已經(jīng)是慢慢地侵蝕每個(gè)...
    lin秀閱讀 280評(píng)論 0 0