亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

Java函數式編程(七):MapReduce

2019-11-26 15:24:31
字體:
來源:轉載
供稿:網友

譯注:map(映射)和reduce(歸約,化簡)是數學上兩個很基礎的概念,它們很早就出現在各類的函數編程語言里了,直到2003年Google將其發揚光大,運用到分布式系統中進行并行計算后,這個組合的名字才開始在計算機界大放異彩(那些函數式粉可能并不這么認為)。本文我們會看到Java 8在搖身一變支持函數式編程后,map和reduce組合的首次亮相(這里只是初步介紹,后續還會有針對它們的專題)。

對集合進行歸約

現在為止我們已經介紹了幾個操作集合的新技巧了:查找匹配元素,查找單個元素,集合轉化。這些操作有一個共同點,它們都是對集合中的單個元素進行操作。不需要對元素進行比較,或者對兩個元素進行運算。本節中我們來看一下如何比較元素,以及在遍歷集合過程中動態維護一個運算結果。

我們先從簡單的例子開始,然后再循序漸進。在第一個例子中,我們先來遍歷一下friends集合,計算出所有名字的總字符數。

復制代碼 代碼如下:

System.out.println("Total number of characters in all names: " + friends.stream()
         .mapToInt(name -> name.length())
         .sum());

要算出所有字符的總數我們得知道每個名字的長度。通過mapToInt()方法可以輕松的完成這個。當我們已經把名字轉化成了對應的長度之后,最后只需要把它們加到一塊就行了。我們有一個內置的sum()方法來完成這個。下面是最后的輸出:

復制代碼 代碼如下:

Total number of characters in all names: 26

我們使用了map操作的一個變種,mapToInt()方法(這種的有mapToInt, mapToDouble等,會對應生成具體類型的流,比如IntStream,DoubleStream),然后根據返回的長度計算出總的字符數。

除了使用sum方法,還有很多類似的方法可以使用,比如用max()可以求出最大的長度,用min()是最小長度,sorted()對長度進行排序,average()求平均長度,等等。

上述這個例子還有一個吸引人的地方就是現在越來越流行的MapReduce模式,map()方法進行映射,而sum()方法是一個比較常用的reduce操作。事實上,JDK中sum()方法的實現用的就是reduce()方法。我們來看下reduce操作更常用的一些形式。

比方說,我們遍歷所有的名字,然后打印出名字最長的那個。如果最長的名字有好幾個,我們就打印出最開始找到的那個。一種方法是,我們計算出最大的長度,然后選出匹配這個長度的第一個元素。不過這樣做需要遍歷兩次列表――效率太低了。這正是reduce操作上場的時候了。

我們可以用reduce操作來比較兩個元素的長度,然后返回最長的那個,再和剩下的元素做進一步比較。跟我們之前看到的別的高階函數一樣,reduce()方法同樣也是遍歷了整個集合。除此之外,它還記錄了lambda表達式返回的計算結果。有個例子的話可以幫助我們更好的理解這點,那我們先來看一段代碼吧。

復制代碼 代碼如下:

final Optional<String> aLongName = friends.stream()
         .reduce((name1, name2) ->
            name1.length() >= name2.length() ? name1 : name2);
aLongName.ifPresent(name ->
System.out.println(String.format("A longest name: %s", name)));

傳給reduce()方法的lambda表達式接收兩個參數,name1和name2,它會比較它們的長度,返回最長的那個。reduce()方法根本不知道我們要干什么。這個邏輯被剝離到我們傳遞進去的lambda表達式里面了――這是策略模式的一個輕量級的實現。

這個lambda表達式正好能適配成JDK中一個BinaryOperator的函數式接口的apply方法。這正是reduce方法要接受的參數類型。我們來運行下這個reduce方法,看看它能否正確地在兩個最長的名字中選出第一個來。

復制代碼 代碼如下:

A longest name: Brian

在reduce()方法遍歷集合的過程中,它先對集合的前兩個元素調用了lambda表達式,調用返回的結果繼續用于下一次調用。在第二次調用中,name1的值被綁定成上次調用的結果,name2的值則是集合的第三個元素。剩余的元素也這樣依次調用下去。最后一次lambda表達式調用的結果,就是整個reduce()方法返回的結果。

reduce()方法返回的是一個Optional值,因為傳遞給它的集合可能是空的。那樣的話,也不存在什么最長的名字了。如果列表只有一個元素,reduce方法直接返回那個元素,不會對lambda表達式進行調用。

從這個例子中我們可以推斷出,reduce的結果最多只可能是集合中的一個元素。如果我們希望能返回一個默認值或者基礎值的話,我們可以使用reduce()方法的一個變種,它可以接收一個額外的參數。比如,如果最短的名字是Steve,我們可以把它傳給reduce()方法,像這樣:

復制代碼 代碼如下:

final String steveOrLonger = friends.stream()
     .reduce("Steve", (name1, name2) ->
            name1.length() >= name2.length() ? name1 : name2);

如果有名字比它長的,那么這個名字會被選中;否則的話就返回這個基礎值Steve。這個版本的reduce()方法不會返回Optional對象,因為如果集合是空的,會返回一個默認值;不用考慮沒有返回值的情況。

在我們結束這章之前 ,我們再來看一下集合操作里面一個很基礎的卻又不是那么容易的操作:合并元素。

合并元素

我們已經學習了如何進行元素的查找,遍歷,以及集合的轉化。不過還有一個常見的操作――將集合元素進行拼接――如果沒有這個新添加的join()函數的話,之前說的簡潔和優雅的代碼只能成為泡影了。這個簡單的方法非常實用以至于它成為JDK里最常用的函數之一。我們來看下如何用它來打印列表中的元素,用逗號進行分隔。

我們還是用這個friends列表。如果用JDK庫里的舊方法的話,想要打印出所有名字并用逗號隔開的話,要做哪些工作?

我們得遍歷列表并且挨個打印元素。Java 5中的for循環比之前的有所改進,我們就用它吧。

復制代碼 代碼如下:

for(String name : friends) {
      System.out.print(name + ", ");
}
System.out.println();

代碼很簡單,我們看下它的輸出是什么。
復制代碼 代碼如下:

Brian, Nate, Neal, Raju, Sara, Scott,

該死,最后多出了一個討厭的逗號(我們難道要怪最后的那個Scott?)。怎么能讓Java別放一個逗號在這呢?不幸的是,循環會按步就班的執行,想讓它在最后特殊處理一下可不容易。為了解決這個問題,我們可以用回原來的那種循環方式。

復制代碼 代碼如下:

for(int i = 0; i < friends.size() - 1; i++) {
      System.out.print(friends.get(i) + ", ");
}
if(friends.size() > 0)
      System.out.println(friends.get(friends.size() - 1));

我們來看下這個版本的輸出是不是OK。
復制代碼 代碼如下:

Brian, Nate, Neal, Raju, Sara, Scott

結果還是不錯的,不過這個代碼就不敢恭維了。救救我們吧,Java。

我們不用再忍受這種痛苦了。Java 8里的StringJoiner類幫我們搞定了這些難題,不止如此,String類還增加了一個join方法方便我們可以用一行代碼來替代掉上面那坨東西。

復制代碼 代碼如下:

System.out.println(String.join(", ", friends));

快來看下吧,結果跟代碼一樣令人滿意。
復制代碼 代碼如下:

Brian, Nate, Neal, Raju, Sara, Scott

結果還是不錯的,不過這個代碼就不敢恭維了。救救我們吧,Java。

我們不用再忍受這種痛苦了。Java 8里的StringJoiner類幫我們搞定了這些難題,不止如此,String類還增加了一個join方法方便我們可以用一行代碼來替代掉上面那坨東西。

復制代碼 代碼如下:

System.out.println(String.join(", ", friends));

快來看下吧,結果跟代碼一樣令人滿意。
復制代碼 代碼如下:

Brian, Nate, Neal, Raju, Sara, Scott

在底層實現中,String.join()方法調用了StringJoiner類來將第二個參數傳進來的值(這是個變長參數)拼接成一個長的字符串,用第一個參數作為分隔符。這個方法當然不止是能拼接逗號這么簡單了。比如說,我們可以傳入一堆路徑,然后很容易的拼出一個類路徑(classpath),這可真是多虧了這些新增加的方法和類。

我們已經知道如何去連接列表元素了,在進行列表連接前,我們還可以先對元素進行轉化,當然我們也知道如何使用map方法來進行列表轉化了。接下來還可以用filter()方法過濾出我們想要的那些元素。最后一步的連接列表元素,用逗號還是什么分隔符,不過就是一個簡單的reduce操作而已了。

我們可以用reduce()方法將元素拼接成一個字符串,不過這需要我們費點工夫。JDK有一個十分方便的collect()方法,它也是reduce()的一個變種,我們可以用它來把元素合并成一個想要的值。

collect()方法來執行歸約操作,不過它把具體的操作委托給一個collector來執行。我們可以把轉化后的元素合并成一個ArrayList。繼續剛才那個例子,我們可以將轉化后的元素,拼接成一個用逗號分隔的字符串。

復制代碼 代碼如下:

System.out.println(
      friends.stream()
          .map(String::toUpperCase)
          .collect(joining(", ")));

我們在轉化后的列表上調用了collect()方法,給它傳入了一個joining()方法返回的collector,joining是Collectors工具類里的一個靜態方法。collector就像是個接收器,它接收collect傳進來的對象,并把它們存儲成你想要的格式:ArrayList, String等。我們會在52頁的collect方法及Collectors類中進一步探索這個方法。

這是輸出的名字,現在它們是大寫的,并用逗號隔開。

復制代碼 代碼如下:

BRIAN, NATE, NEAL, RAJU, SARA, SCOTT

總結

集合在編程中十分常見,有了lambda表達式后,Java的集合操作變得更加簡單容易了。那些拖沓的集合操作的老代碼都可以換成這種優雅簡潔的新方式。內部迭代器使得集合遍歷,轉化都變得更加方便,遠離了可變性的煩惱,查找集合元素也變得異常輕松。使用這些新方法可以少寫不少代碼。這使得代碼更容易維護,更聚焦于業務邏輯,編程中的那些基本操作也變得更少了。

下一章中我們會看到lambda表達式如何簡化程序開發中的另一個基本操作:字符串操作以及對象比較。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
在线观看久久av| 精品成人久久av| 欧美性xxxx极品高清hd直播| 国产精品一久久香蕉国产线看观看| 7777免费精品视频| 国产日本欧美一区二区三区| 欧美成人免费一级人片100| 中文字幕亚洲欧美| 久久国产精品免费视频| 日韩视频在线免费观看| 亚洲伊人成综合成人网| 国产精品男人的天堂| 日韩av在线电影网| 久久在线精品视频| 亚洲一区二区久久久| 亚洲欧洲av一区二区| 亚洲色图综合久久| 黑人巨大精品欧美一区二区免费| 亚洲一区二区三区在线免费观看| 亚洲天堂一区二区三区| 奇门遁甲1982国语版免费观看高清| 国产69久久精品成人看| 国产精品视频一区二区三区四| 国产福利视频一区二区| 日韩国产中文字幕| 久久久噜噜噜久久| 亚洲成人黄色在线观看| 自拍亚洲一区欧美另类| 欧美老女人www| 成人两性免费视频| 亚洲精品小视频在线观看| 91av在线网站| 成人免费看片视频| 7m第一福利500精品视频| 久热精品视频在线| 欧美插天视频在线播放| 欧美二区乱c黑人| 91在线视频免费| 午夜精品久久久久久久白皮肤| 亚洲国产精品成人av| 日韩毛片中文字幕| 日韩美女视频免费在线观看| 最近2019中文字幕mv免费看| 欧美黑人狂野猛交老妇| 国产精品久久97| 欧美天堂在线观看| 国模精品一区二区三区色天香| 亚洲第一男人天堂| 欧美激情免费在线| 九九久久久久99精品| 久久中文字幕在线| 亚洲午夜精品久久久久久久久久久久| 国产福利视频一区| 日韩三级影视基地| 97精品久久久中文字幕免费| 日韩av在线免费| xxx一区二区| 国产亚洲欧美日韩一区二区| 青青草原成人在线视频| 国产精品入口尤物| 国产福利视频一区| 久久久久久有精品国产| 欧美天天综合色影久久精品| 亚洲老头老太hd| 亚洲综合一区二区不卡| 欧美日韩在线观看视频| 91免费观看网站| 久久天天躁狠狠躁夜夜爽蜜月| 国产精品欧美一区二区三区奶水| 91性高湖久久久久久久久_久久99| 97久久伊人激情网| 亚洲大胆人体av| 国产成人精品免费久久久久| 日韩精品高清在线观看| 久久久久久国产三级电影| 日本精品一区二区三区在线| 欧美午夜精品在线| 久久亚洲私人国产精品va| 亚洲高清在线观看| 成人xxxxx| 最新69国产成人精品视频免费| 中文字幕在线视频日韩| 午夜精品一区二区三区在线视| 日韩在线视频导航| 欧美日本高清一区| 国产欧美日韩丝袜精品一区| 搡老女人一区二区三区视频tv| 日韩一区av在线| 永久免费精品影视网站| 国产网站欧美日韩免费精品在线观看| 亚洲一区二区免费在线| 亚洲福利在线观看| 亚洲人成在线观看| 久久艹在线视频| 国产成人拍精品视频午夜网站| 91久久精品美女高潮| 日韩美女激情视频| 国产精品第七十二页| 大胆人体色综合| 在线视频欧美性高潮| 亚洲美女自拍视频| 亚洲人成77777在线观看网| 亚洲视频自拍偷拍| 亚洲电影免费观看高清完整版| 日韩高清电影好看的电视剧电影| 国产免费一区二区三区在线观看| 国外日韩电影在线观看| 欧美成人激情视频免费观看| 国产999精品久久久影片官网| 欧美激情久久久久久| 欧洲亚洲免费视频| 国产日韩欧美在线| 久久久天堂国产精品女人| 国产成+人+综合+亚洲欧美丁香花| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲综合成人婷婷小说| 91av在线精品| 日韩久久午夜影院| 亚洲精品wwww| 一区二区三区视频免费在线观看| 久久中文字幕在线| 国产精品久久久av| 亚洲午夜精品久久久久久久久久久久| 精品免费在线观看| 欧洲成人免费视频| 国产精品盗摄久久久| 成人精品aaaa网站| 国产一区二区美女视频| 亚洲最新视频在线| 精品人伦一区二区三区蜜桃免费| 日韩精品福利网站| 亚洲欧美在线磁力| 日韩福利视频在线观看| 国产一区二区三区视频免费| 俺去亚洲欧洲欧美日韩| 亚洲成人三级在线| 岛国av一区二区在线在线观看| 国产精品吴梦梦| 欧美电影在线观看高清| 亚洲欧美制服中文字幕| 91爱爱小视频k| 欧美日本黄视频| 久久亚洲精品成人| 51精品在线观看| 久久综合久久八八| 欧美影院成年免费版| 日韩欧美黄色动漫| 色哟哟入口国产精品| 国产精品在线看| 91精品久久久久久| 久久精品国产亚洲| 国产精品视频白浆免费视频| 久久精品成人一区二区三区| 亚洲欧美日韩第一区| 国产精品夜间视频香蕉| 欧美午夜xxx| 久久精品国产96久久久香蕉| 亚洲综合中文字幕在线| 国产精品丝袜久久久久久不卡| 色狠狠久久aa北条麻妃| 91精品国产综合久久香蕉的用户体验| 久久国产精彩视频| 色狠狠久久aa北条麻妃| 久久手机免费视频|