Java8學(xué)習(xí)筆記-12:整合操作示例

第一個(gè)問(wèn)題描述:
找出某張專輯上所有樂(lè)隊(duì)的國(guó)籍。藝術(shù)家列表里既有個(gè)人,也有 樂(lè)隊(duì)。利用一點(diǎn)領(lǐng)域知識(shí),假定一般樂(lè)隊(duì)名以定冠詞 The 開(kāi)頭。當(dāng)然這不是絕對(duì)的,但也 差不多。
找出某張專輯上所有樂(lè)隊(duì)的國(guó)籍。藝術(shù)家列表里既有個(gè)人,也有 樂(lè)隊(duì)。假定一般樂(lè)隊(duì)名以定冠詞 The 開(kāi)頭。
首先, 可將這個(gè)問(wèn)題分解為如下幾個(gè)步驟。

  1. 找出專輯上的所有表演者。
  2. 分辨出哪些表演者是樂(lè)隊(duì)。
  3. 找出每個(gè)樂(lè)隊(duì)的國(guó)籍。
  4. 將找出的國(guó)籍放入一個(gè)集合。
    現(xiàn)在,找出每一步對(duì)應(yīng)的 Stream API 就相對(duì)容易了:
  5. Album 類有個(gè) getMusicians 方法,該方法返回一個(gè) Stream 對(duì)象,包含整張專輯中所有的
    表演者;
  6. 使用 filter 方法對(duì)表演者進(jìn)行過(guò)濾,只保留樂(lè)隊(duì);
  7. 使用 map 方法將樂(lè)隊(duì)映射為其所屬國(guó)家;
  8. 使用 collect(Collectors.toList()) 方法將國(guó)籍放入一個(gè)列表。
    最后,整合所有的操作,就得到如下代碼:
     Set<String> origins = album.getMusicians()
                                .filter(artist -> artist.getName().startsWith("The"))
                                .map(artist -> artist.getNationality())
                                .collect(toSet());

通過(guò)重構(gòu)將現(xiàn)有代碼改寫(xiě)為L(zhǎng)ambada形式
問(wèn)題描述:假定選定一組專輯,找出其中所有長(zhǎng)度大于 1 分鐘的曲目名稱

//遺留代碼:
public Set<String> findLongTracks(List<Album> albums) { 
      Set<String> trackNames = new HashSet<>();
        for (Album album : albums) {
            for (Track track : album.getTrackList()) {
                if (track.getLength() > 60) {
                    String name = track.getName();
                    trackNames.add(name);
                }
            }
        }
        return trackNames;
```
如果仔細(xì)閱讀上面的這段代碼,就會(huì)發(fā)現(xiàn)幾組嵌套的循環(huán)。僅通過(guò)閱讀這段代碼很難看出 它的編寫(xiě)目的,那就來(lái)重構(gòu)一下(使用流來(lái)重構(gòu)該段代碼的方式很多,下面介紹的只是其 中一種。事實(shí)上,對(duì) Stream API 越熟悉,就越不需要細(xì)分步驟。之所以在示例中一步一步 地重構(gòu),完全是出于幫助大家學(xué)習(xí)的目的,在工作中無(wú)需這樣做)。
第一步要修改的是 for 循環(huán)。首先使用 Stream 的 forEach 方法替換掉 for 循環(huán),但還是暫 時(shí)保留原來(lái)循環(huán)體中的代碼,這是在重構(gòu)時(shí)非常方便的一個(gè)技巧。調(diào)用 stream 方法從專輯 列表中生成第一個(gè) Stream,同時(shí)不要忘了在上一節(jié)已介紹過(guò),getTracks 方法本身就返回 一個(gè) Stream 對(duì)象。經(jīng)過(guò)第一步重構(gòu)后,代碼如下所示:
```
//第一次重構(gòu):找出長(zhǎng)度大于 1 分鐘的曲目
public Set<String> findLongTracks(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        albums.stream()
                .forEach(album -> {
                    album.getTracks()
                            .forEach(track -> {
                                if (track.getLength() > 60) {
                                    String name = track.getName();
                                    trackNames.add(name);
                                }
                            });
                });
        return trackNames;
    }
```
在第一次重構(gòu)中,雖然使用了流,但是并沒(méi)有充分發(fā)揮它的作用。事實(shí)上,重構(gòu)后的代 碼還不如原來(lái)的代碼好——天哪!因此,是時(shí)候引入一些更符合流風(fēng)格的代碼了,最內(nèi)層 的 forEach 方法正是主要突破口。
最內(nèi)層的 forEach 方法有三個(gè)功用:找出長(zhǎng)度大于 1 分鐘的曲目,得到符合條件的曲目名 稱,將曲目名稱加入集合 Set。這就意味著需要三項(xiàng) Stream 操作:找出滿足某種條件的曲 目是 filter 的功能,得到曲目名稱則可用 map 達(dá)成,終結(jié)操作可使用 forEach 方法將曲目
名稱加入一個(gè)集合。用以上三項(xiàng) Stream 操作將內(nèi)部的 forEach 方法拆分后,代碼如下所示:
```
//第二次重構(gòu):找出長(zhǎng)度大于 1 分鐘的曲目
public Set<String> findLongTracks(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        albums.stream()
                .forEach(album -> {
                    album.getTracks()
                            .filter(track -> track.getLength() > 60)
                            .map(track -> track.getName())
                            .forEach(name -> trackNames.add(name));
                });
        return trackNames;
    }
```
現(xiàn)在用更符合流風(fēng)格的操作替換了內(nèi)層的循環(huán),但代碼看起來(lái)還是冗長(zhǎng)繁瑣。將各種流嵌 套起來(lái)并不理想,最好還是用干凈整潔的順序調(diào)用一些方法。
理想的操作莫過(guò)于找到一種方法,將專輯轉(zhuǎn)化成一個(gè)曲目的 Stream。眾所周知,任何時(shí)候 想轉(zhuǎn)化或替代代碼,都該使用 map 操作。這里將使用比 map 更復(fù)雜的 flatMap 操作,把多個(gè) Stream 合并成一個(gè) Stream 并返回。將 forEach 方法替換成 flatMap 后,代碼如下所示:
```
//第三次重構(gòu):找出長(zhǎng)度大于 1 分鐘的曲目
public Set<String> findLongTracks(List<Album> albums) {
        Set<String> trackNames = new HashSet<>();
        albums.stream()
                .flatMap(album -> album.getTracks())
                .filter(track -> track.getLength() > 60)
                .map(track -> track.getName())
                .forEach(name -> trackNames.add(name));
        return trackNames;
    }
```
上面的代碼中使用一組簡(jiǎn)潔的方法調(diào)用替換掉兩個(gè)嵌套的 for 循環(huán),看起來(lái)清晰很多。然 而至此并未結(jié)束,仍需手動(dòng)創(chuàng)建一個(gè) Set 對(duì)象并將元素加入其中,但我們希望看到的是整 個(gè)計(jì)算任務(wù)由一連串的 Stream 操作完成。
到目前為止,雖然還未展示轉(zhuǎn)換的方法,但已有類似的操作。就像使用 collect(Collectors. toList()) 可以將 Stream 中的值轉(zhuǎn)換成一個(gè)列表,使用 collect(Collectors.toSet()) 可以將 Stream 中的值轉(zhuǎn)換成一個(gè)集合。因此,將最后的 forEach 方法替換為 collect,并刪掉變量 trackNames,代碼如下所示:
```
//第四次重構(gòu):找出長(zhǎng)度大于 1 分鐘的曲目
public Set<String> findLongTracks(List<Album> albums) {
        return albums.stream()
                .flatMap(album -> album.getTracks())
                .filter(track -> track.getLength() > 60)
                .map(track -> track.getName())
                .collect(Collectors.toSet());
    }
```
簡(jiǎn)而言之,選取一段遺留代碼進(jìn)行重構(gòu),轉(zhuǎn)換成使用流風(fēng)格的代碼。最初只是簡(jiǎn)單地使用流,但沒(méi)有引入任何有用的流操作。隨后通過(guò)一系列重構(gòu),最終使代碼更符合使用流的風(fēng) 格。在上述步驟中我們沒(méi)有提到一個(gè)重點(diǎn),即編寫(xiě)示例代碼的每一步都要進(jìn)行單元測(cè)試, 保證代碼能夠正常工作。重構(gòu)遺留代碼時(shí),這樣做很有幫助。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,414評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,169評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,722評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,465評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,823評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,000評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,554評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,513評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,722評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,125評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,430評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,237評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,482評(píng)論 2 379

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

  • 扯淡 在準(zhǔn)備學(xué) Java8 之前,我以為不會(huì)很難。所以,我就決定一邊學(xué) Java8,一邊寫(xiě)博客。當(dāng)我準(zhǔn)備寫(xiě)這篇博客...
    小白聊技術(shù)閱讀 439評(píng)論 0 1
  • 5.1 方法引用 Lambda 表達(dá)式有一個(gè)常見(jiàn)的用法;Lanbda 表達(dá)式經(jīng)常 調(diào)用參數(shù)。比如想得到藝術(shù)家的姓名...
    伊凡的一天閱讀 1,300評(píng)論 0 3
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,826評(píng)論 18 139
  • 3.1 從外部迭代到內(nèi)部迭代 外部迭代 使用迭代器計(jì)算來(lái)自倫敦的藝術(shù)家人數(shù): int count=0;for (A...
    伊凡的一天閱讀 425評(píng)論 0 2
  • Int Double Long 設(shè)置特定的stream類型, 提高性能,增加特定的函數(shù) 無(wú)存儲(chǔ)。stream不是一...
    patrick002閱讀 1,282評(píng)論 0 0