最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java函數(shù)式編程(十):收集器

 更新時(shí)間:2014年09月26日 09:27:59   作者:有孚  
這篇文章主要介紹了Java函數(shù)式編程(十):收集器,本文是系列文章的第10篇,其它文章請(qǐng)參閱本文底部的相關(guān)文章,需要的朋友可以參考下

前面我們已經(jīng)用過(guò)幾次collect()方法來(lái)將Stream返回的元素拼成ArrayList了。這是一個(gè)reduce操作,它對(duì)于將一個(gè)集合轉(zhuǎn)化成另一種類(lèi)型(通常是一個(gè)可變的集合)非常有用。collect()函數(shù),如果和Collectors工具類(lèi)里的一些方法結(jié)合起來(lái)使用的話(huà),能提供極大的便利性,本節(jié)我們將會(huì)介紹到。

我們還是繼續(xù)使用前面的Person列表作為例子,來(lái)看一下collect()方法到底有哪些能耐。假設(shè)我們要從原始列表中找出所有大于20歲的人。下面是使用了可變性和forEach()方法實(shí)現(xiàn)的版本:

復(fù)制代碼 代碼如下:

List<Person> olderThan20 = new ArrayList<>(); people.stream()
        .filter(person -> person.getAge() > 20)
.forEach(person -> olderThan20.add(person)); System.out.println("People older than 20: " + olderThan20);

我們使用filter()方法來(lái)從列表中過(guò)濾出了所有年齡大于20的人。然后,在forEach方法里,我們將元素添加到一個(gè)在前面已經(jīng)初始化好的ArrayList中。我們先看下這段代碼的輸出結(jié)果,一會(huì)兒再去重構(gòu)它。

復(fù)制代碼 代碼如下:

People older than 20: [Sara - 21, Jane - 21, Greg - 35]

程序輸出的結(jié)果是對(duì)的,不過(guò)還有點(diǎn)小問(wèn)題。首先,把元素添加到集合中,這種屬于低級(jí)操作——它是命令式的,而非聲明式的。如果我們想把這個(gè)迭代改造成并發(fā)的,還得去考慮線(xiàn)程安全的問(wèn)題——可變性使得它難以并行化。幸運(yùn)的是,使用collect()方法可以很容易解決掉這個(gè)問(wèn)題。來(lái)看下如何實(shí)現(xiàn)的。

collect()方法接受一個(gè)Stream并將它們收集到一個(gè)結(jié)果容器中。要完成這個(gè)工作,它需要知道三個(gè)東西:

+如何創(chuàng)建結(jié)果容器(比如說(shuō),使用ArrayList::new方法) +如何把單個(gè)元素添加到容器中(比如使用ArrayList::add方法) +如何把一個(gè)結(jié)果集合并到另一個(gè)中(比如使用ArrayList::addAll方法)

對(duì)于串行操作而言,最后一條不是必需的;代碼設(shè)計(jì)的目標(biāo)是能同時(shí)支持串行和并行的。

我們把這些操作提供給collect方法,讓它來(lái)把過(guò)濾后的流給收集起來(lái)。

復(fù)制代碼 代碼如下:

List<Person> olderThan20 =
people.stream()
.filter(person -> person.getAge() > 20)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println("People older than 20: " + olderThan20);

這段代碼的結(jié)果和前面一樣,不過(guò)這樣寫(xiě)有諸多好處。

首先,我們編程的方式更聚焦了,表述性也更強(qiáng),清晰的傳達(dá)了你要把結(jié)果收集到一個(gè)ArrayList里去的目的。collect()的第一個(gè)參數(shù)是一工廠或者生產(chǎn)者,后面的參數(shù)是一個(gè)用來(lái)收集元素的操作。

第二,由于我們沒(méi)有在代碼中個(gè)執(zhí)行顯式的修改操作,可以很容易并行地執(zhí)行這個(gè)迭代。我們讓底層庫(kù)來(lái)完成修改操作,它自己會(huì)處理好協(xié)作及線(xiàn)程安全的問(wèn)題,盡管ArrayList本身不是線(xiàn)程安全的——干的漂亮。

如果條件允許的話(huà),collect()方法可以并行地將元素添加到不同的子列表中,然后再用一個(gè)線(xiàn)程安全的方式將它們合并到一個(gè)大列表里(最后一個(gè)參數(shù)就是用來(lái)進(jìn)行合并操作的)。

我們已經(jīng)看到,相對(duì)于手動(dòng)把元素添加到列表而言,使用collect()方法的好處真是太多了。下面我們來(lái)看下這個(gè)方法的一個(gè)重載的版本——它更簡(jiǎn)單也更方便——它是使用一個(gè)Collector作為參數(shù)。這個(gè)Collector是一個(gè)包含了生產(chǎn)者,添加器,以及合并器在內(nèi)的接口——在前面的版本中這些操作是作為獨(dú)立的參數(shù)分別傳入方法中的——使用Collector則更簡(jiǎn)單并且可以復(fù)用。Collectors工具類(lèi)提供了一個(gè)toList方法,可以生成一個(gè)Collector的實(shí)現(xiàn),用來(lái)把元素添加到ArrayList中。我們來(lái)修改下前面那段代碼,使用一下這個(gè)collect()方法。

復(fù)制代碼 代碼如下:

List<Person> olderThan20 =
people.stream()
.filter(person -> person.getAge() > 20)
.collect(Collectors.toList());
System.out.println("People older than 20: " + olderThan20);

使用了Collectors工具類(lèi)的簡(jiǎn)潔版的collect()方法,可不止這一種用法。Collectors工具類(lèi)中還有好幾種不同的方法來(lái)可以進(jìn)行不同的收集和添加的操作。比如說(shuō),除了toList()方法,還有toSet()方法,可以添加到一個(gè)Set中,toMap()方法可以用來(lái)收集到一個(gè)key-value的集合中,還有joining()方法,可以拼接成一個(gè)字符串。我們還可以將mapping(),collectingAndThen(),minBy(), maxBy()和groupingBy()等方法組合起來(lái)進(jìn)行使用。

我們來(lái)用下groupingBy()方法來(lái)將人群按年齡進(jìn)行分組。

復(fù)制代碼 代碼如下:

Map<Integer, List<Person>> peopleByAge =
people.stream()
.collect(Collectors.groupingBy(Person::getAge));
System.out.println("Grouped by age: " + peopleByAge);

只需簡(jiǎn)單的調(diào)用下collect()方法便能完成分組。groupingBy()接受一個(gè)lambda表達(dá)式或者方法引用——這種叫分類(lèi)函數(shù)——它返回需要分組的對(duì)象的某個(gè)屬性的值。根據(jù)我們這個(gè)函數(shù)返回的值,來(lái)把調(diào)用上下文中的元素放進(jìn)某個(gè)分組中。在輸出中可以看到分組的結(jié)果:

復(fù)制代碼 代碼如下:

Grouped by age: {35=[Greg - 35], 20=[John - 20], 21=[Sara - 21, Jane - 21]}

這些人已經(jīng)按年齡進(jìn)行了分組。

在前面這個(gè)例子中我們按人群的年齡對(duì)他們進(jìn)行了分組收集。groupingBy()方法的一個(gè)變種可以按多個(gè)條件進(jìn)行分組。簡(jiǎn)單的groupingBy()方法使用了分類(lèi)器進(jìn)行元素收集。而通用的groupingBy()收集器,則可以為每一個(gè)分組指定一個(gè)收集器。也就是說(shuō),元素在收集的過(guò)程中會(huì)途經(jīng)不同的分類(lèi)器和集合,下面我們將會(huì)看到。

繼續(xù)使用上面這個(gè)例子,這回我們不按年齡分組了,我們只獲取人的名字,按他們的年齡進(jìn)行排序。

復(fù)制代碼 代碼如下:

Map<Integer, List<String>> nameOfPeopleByAge =
people.stream()
.collect(
groupingBy(Person::getAge, mapping(Person::getName, toList())));
System.out.println("People grouped by age: " + nameOfPeopleByAge);

這個(gè)版本的groupingBy()接受兩個(gè)參數(shù):第一個(gè)是年齡,這是分組的條件,第二個(gè)是一個(gè)收集器,它是由mapping()函數(shù)返回的結(jié)果。這些方法都來(lái)自Collectors工具類(lèi),在這段代碼中進(jìn)行了靜態(tài)的導(dǎo)入。mapping()方法接受兩個(gè)參數(shù),一個(gè)是映射用的屬性,一個(gè)是對(duì)象要收集到的地方,比如說(shuō)list或者set。來(lái)看下上面這段代碼的輸出結(jié)果:

復(fù)制代碼 代碼如下:

People grouped by age: {35=[Greg], 20=[John], 21=[Sara, Jane]}

可以看到,人們的名字已經(jīng)按年齡進(jìn)行分組了。

我們?cè)賮?lái)看一個(gè)組合的操作:按名字的首字母進(jìn)行分組,然后選出每個(gè)分組中年紀(jì)最大的那位。

復(fù)制代碼 代碼如下:

Comparator<Person> byAge = Comparator.comparing(Person::getAge);
Map<Character, Optional<Person>> oldestPersonOfEachLetter =
people.stream()
.collect(groupingBy(person -> person.getName().charAt(0),
reducing(BinaryOperator.maxBy(byAge))));
System.out.println("Oldest person of each letter:");
System.out.println(oldestPersonOfEachLetter);

我們先是按名字的首字母進(jìn)行了排序。為了實(shí)現(xiàn)這個(gè),我們把一個(gè)lambda表達(dá)式作為groupingBy()的第一個(gè)參數(shù)傳了進(jìn)去。這個(gè)lambda表達(dá)式是用來(lái)返回名字的首字母的,以便進(jìn)行分組。第二個(gè)參數(shù)不再是mapping()了,而是執(zhí)行了一個(gè)reduce操作。在每個(gè)分組內(nèi),它使用maxBy()方法,從所有元素中遞推出最年長(zhǎng)的那位。由于組合了許多操作,這個(gè)語(yǔ)法看起來(lái)有點(diǎn)臃腫,不過(guò)整個(gè)讀起來(lái)是這樣的:按名字首字母進(jìn)行分組,然后遞推出分組中最年長(zhǎng)的那位。來(lái)看下這段代碼的輸出,它列出了指定字母開(kāi)頭的那組名字中年紀(jì)最大的那個(gè)人。

復(fù)制代碼 代碼如下:

Oldest person of each letter:
{S=Optional[Sara - 21], G=Optional[Greg - 35], J=Optional[Jane - 21]}

我們已經(jīng)領(lǐng)教到了collect()方法以及Collectors工具類(lèi)的威力。在你的IDE或者JDK的官方文檔里面,再花點(diǎn)時(shí)間去研究下Collectors工具類(lèi)吧,熟悉下它提供的各種方法。下面我們將會(huì)用lambda表達(dá)式來(lái)完成一些過(guò)濾器的實(shí)現(xiàn)。

相關(guān)文章

最新評(píng)論

株洲县| 崇义县| 广平县| 甘南县| 怀柔区| 田东县| 平远县| 莱芜市| 晴隆县| 渝北区| 洮南市| 广昌县| 介休市| 佛坪县| 梨树县| 富锦市| 天全县| 旬邑县| 夹江县| 桦南县| 万荣县| 霍林郭勒市| 耿马| 手游| 忻州市| 通榆县| 崇州市| 长岛县| 抚顺市| 枣强县| 濮阳市| 林西县| 平陆县| 洪泽县| 谢通门县| 沁源县| 繁峙县| 布尔津县| 新干县| 九江市| 诸暨市|