Java8新特性第3章(Stream API)

圖片來(lái)自網(wǎng)絡(luò)

轉(zhuǎn)載請(qǐng)注明出處:http://www.reibang.com/p/e3ba9a0b7d72


Stream作為Java8的新特性之一,他與Java IO包中的InputStream和OutputStream完全不是一個(gè)概念涌矢。Java8中的Stream是對(duì)集合功能的一種增強(qiáng)摆出,主要用于對(duì)集合對(duì)象進(jìn)行各種非常便利高效的聚合和大批量數(shù)據(jù)的操作朗徊。結(jié)合Lambda表達(dá)式可以極大的提高開發(fā)效率和代碼可讀性。

假設(shè)我們需要把一個(gè)集合中的所有形狀設(shè)置成紅色偎漫,那么我們可以這樣寫

for (Shape shape : shapes){
    shape.setColor(RED)
}

如果使用Java8擴(kuò)展后的集合框架則可以這樣寫:

shapes.foreach(s -> s.setColor(RED));

第一種寫法我們叫外部迭代爷恳,for-each調(diào)用shapesiterator()依次遍歷集合中的元素。這種外部迭代有一些問題:

  • for循環(huán)是串行的象踊,而且必須按照集合中元素的順序依次進(jìn)行温亲;
  • 集合框架無(wú)法對(duì)控制流進(jìn)行優(yōu)化,例如通過排序通危、并行铸豁、短路求值以及惰性求值改善性能。

上面這兩個(gè)問題我們會(huì)在后面的文章中逐步解答菊碟。

第二種寫法我們叫內(nèi)部迭代节芥,兩段代碼雖然看起來(lái)只是語(yǔ)法上的區(qū)別,但實(shí)際上他們內(nèi)部的區(qū)別其實(shí)非常大。用戶把對(duì)操作的控制權(quán)交還給類庫(kù)头镊,從而允許類庫(kù)進(jìn)行各種各樣的優(yōu)化(例如亂序執(zhí)行蚣驼、惰性求值和并行等等)∠嗤В總的來(lái)說(shuō)颖杏,內(nèi)部迭代使得外部迭代中不可能實(shí)現(xiàn)的優(yōu)化成為可能。

外部迭代同時(shí)承擔(dān)了做什么(把形狀設(shè)為紅色)和怎么做(得到Iterator實(shí)例然后依次遍歷)坛芽,而內(nèi)部迭代只負(fù)責(zé)做什么留储,而把怎么做留給類庫(kù)。這樣代碼會(huì)變得更加清晰咙轩,而集合類庫(kù)則可以在內(nèi)部進(jìn)行各種優(yōu)化获讳。

1.什么是Stream

Stream不是集合元素,它也不是數(shù)據(jù)結(jié)構(gòu)活喊、不能保存數(shù)據(jù)丐膝,它更像一個(gè)更高級(jí)的Interator。Stream提供了強(qiáng)大的數(shù)據(jù)集合操作功能钾菊,并被深入整合到現(xiàn)有的集合類和其它的JDK類型中帅矗。流的操作可以被組合成流水線(Pipeline)。拿前面的例子來(lái)說(shuō)煞烫,如果我只想把藍(lán)色改成紅色:

shapes.stream()
      .filter(s -> s.getColor() == BLUE)
      .forEach(s -> s.setColor(RED));

Collection上調(diào)用stream()會(huì)生成該集合元素的流浑此,接下來(lái)filter()操作會(huì)產(chǎn)生只包含藍(lán)色形狀的流,最后红竭,這些藍(lán)色形狀會(huì)被forEach操作設(shè)為紅色尤勋。

如果我們想把藍(lán)色的形狀提取到新的List里喘落,則可以:

List<Shape> blue = shapes.stream()
                          .filter(s -> s.getColor() == BLUE)
                          .collect(Collectors.toList());

collect()操作會(huì)把其接收的元素聚集到一起(這里是List)茵宪,collect()方法的參數(shù)則被用來(lái)指定如何進(jìn)行聚集操作。在這里我們使用toList()以把元素輸出到List中瘦棋。

如果每個(gè)形狀都被保存在Box里稀火,然后我們想知道哪個(gè)盒子至少包含一個(gè)藍(lán)色形狀,我們可以這么寫:

Set<Box> hasBlueShape = shapes.stream()
                               .filter(s -> s.getColor() == BLUE)
                              .map(s -> s.getContainingBox())
                              .collect(Collectors.toSet());

map()操作通過映射函數(shù)(這里的映射函數(shù)接收一個(gè)形狀赌朋,然后返回包含它的盒子)對(duì)輸入流里面的元素進(jìn)行依次轉(zhuǎn)換凰狞,然后產(chǎn)生新流。

如果我們需要得到藍(lán)色物體的總重量沛慢,我們可以這樣表達(dá):

int sum = shapes.stream()
                .filter(s -> s.getColor() == BLUE)
                .mapToInt(s -> s.getWeight())
                .sum();

2.Stream vs Collection

流(Stream)和集合(Collection)的區(qū)別:

  • Collection主要用來(lái)對(duì)元素進(jìn)行管理和訪問赡若;
  • Stream并不支持對(duì)其元素進(jìn)行直接操作和直接訪問,而只支持通過聲明式操作在其之上進(jìn)行運(yùn)算后得到結(jié)果团甲;
  • Stream不存儲(chǔ)值
  • 對(duì)Stream的操作會(huì)產(chǎn)生一個(gè)結(jié)果逾冬,但是Stream并不會(huì)改變數(shù)據(jù)源;
  • 大多數(shù)Stream的操作(filter,map,sort等)都是以惰性的方式實(shí)現(xiàn)的。這使得我們可以使用一次遍歷完成整個(gè)流水線操作,并可以用短路操作提供更高效的實(shí)現(xiàn)身腻。

3.惰性求值 vs 急性求值

filter()map()這樣的操作既可以被急性求值(以filter()為例产还,急性求值需要在方法返回前完成對(duì)所有元素的過濾),也可以被惰性求值(用Stream代表過濾結(jié)果嘀趟,當(dāng)且僅當(dāng)需要時(shí)才進(jìn)行過濾操作)在實(shí)際中進(jìn)行惰性運(yùn)算可以帶來(lái)很多好處脐区。比如說(shuō),如果我們進(jìn)行惰性過濾她按,我們就可以把過濾和流水線里的其它操作混合在一起牛隅,從而不需要對(duì)數(shù)據(jù)進(jìn)行多遍遍歷。相類似的酌泰,如果我們?cè)谝粋€(gè)大型集合里搜索第一個(gè)滿足某個(gè)條件的元素倔叼,我們可以在找到后直接停止,而不是繼續(xù)處理整個(gè)集合宫莱。(這一點(diǎn)對(duì)無(wú)限數(shù)據(jù)源是很重要丈攒,惰性求值對(duì)于有限數(shù)據(jù)源起到的是優(yōu)化作用,但對(duì)無(wú)限數(shù)據(jù)源起到的是決定作用授霸,沒有惰性求值巡验,對(duì)無(wú)限數(shù)據(jù)源的操作將無(wú)法終止)

對(duì)于filter()map()這樣的操作,我們很自然的會(huì)把它當(dāng)成是惰性求值操作碘耳,不過它們是否真的是惰性取決于它們的具體實(shí)現(xiàn)显设。另外,像sum()這樣生成值的操作和forEach()這樣產(chǎn)生副作用的操作都是天然急性求值辛辨,因?yàn)樗鼈儽仨氁a(chǎn)生具體的結(jié)果捕捂。

我們拿下面這段代碼舉例:

int sum = shapes.stream()
                .filter(s -> s.getColor() == BLUE)
                .mapToInt(s -> s.getWeight())
                .sum();

這里的filter()map()都是惰性的,這就意味著在調(diào)用sum()之前不會(huì)從數(shù)據(jù)源中提取任何元素斗搞。在sum()操作之后才會(huì)把filter()指攒、map()sum()放在對(duì)數(shù)據(jù)源一次遍歷中。這樣可以大大減少維持中間結(jié)果所帶來(lái)的開銷僻焚。

4.舉個(gè)栗子??

前面長(zhǎng)篇大論的介紹概念實(shí)在太枯燥允悦,為了方便大家理解我們用Streams API來(lái)實(shí)現(xiàn)一個(gè)具體的業(yè)務(wù)場(chǎng)景。

假設(shè)我們有一個(gè)房源庫(kù)項(xiàng)目虑啤,這個(gè)房源庫(kù)中有一系列的小區(qū)隙弛,每個(gè)小區(qū)都有小區(qū)名和房源列表,每套房子又有價(jià)格狞山、面積等屬性∪疲現(xiàn)在我們需要篩選出含有100平米以上房源的小區(qū),并按照小區(qū)名排序萍启。

我們先來(lái)看看不用Streams API如何實(shí)現(xiàn):

List<Community> result = new ArrayList<>();
for (Community community : communities) {
        for (House house : community.houses) {
            if (house.area > 100) {
                result.add(community);
                break;
            }
        }
    }
    Collections.sort(result, new Comparator<Community>() {
        @Override
        public int compare(Community c1, Community c2) {
            return c1.name.compareTo(c2.name);
        }
    });
    return result;

如果使用Streams API:

return communities.stream()
                  .filter(c -> c.houses.stream().anyMatch(h -> h.area>100))
                  .sorted(Comparator.comparing(c -> c.name))
                  .collect(Collectors.toList());
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末总珠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姚淆,老刑警劉巖孕蝉,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異腌逢,居然都是意外死亡降淮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門搏讶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)佳鳖,“玉大人,你說(shuō)我怎么就攤上這事媒惕∠捣裕” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵妒蔚,是天一觀的道長(zhǎng)穿挨。 經(jīng)常有香客問我,道長(zhǎng)肴盏,這世上最難降的妖魔是什么科盛? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮菜皂,結(jié)果婚禮上贞绵,老公的妹妹穿的比我還像新娘。我一直安慰自己恍飘,他們只是感情好榨崩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著章母,像睡著了一般母蛛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胳施,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天溯祸,我揣著相機(jī)與錄音肢专,去河邊找鬼舞肆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛博杖,可吹牛的內(nèi)容都是我干的椿胯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼剃根,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哩盲!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤廉油,失蹤者是張志新(化名)和其女友劉穎惠险,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抒线,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡班巩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘶炭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抱慌。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖眨猎,靈堂內(nèi)的尸體忽然破棺而出抑进,到底是詐尸還是另有隱情,我是刑警寧澤睡陪,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布寺渗,位于F島的核電站,受9級(jí)特大地震影響兰迫,放射性物質(zhì)發(fā)生泄漏户秤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一逮矛、第九天 我趴在偏房一處隱蔽的房頂上張望鸡号。 院中可真熱鬧,春花似錦须鼎、人聲如沸鲸伴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汞窗。三九已至,卻和暖如春赡译,著一層夾襖步出監(jiān)牢的瞬間仲吏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蝌焚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裹唆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓只洒,卻偏偏與公主長(zhǎng)得像许帐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子毕谴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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