激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Java函數式編程(十):收集器

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

2019-12-01 12:47有孚 JAVA教程

這篇文章主要介紹了Java函數式編程(十):收集器,本文是系列文章的第10篇,其它文章請參閱本文底部的相關文章,需要的朋友可以參考下

前面我們已經用過幾次collect()方法來將Stream返回的元素拼成ArrayList了。這是一個reduce操作,它對于將一個集合轉化成另一種類型(通常是一個可變的集合)非常有用。collect()函數,如果和Collectors工具類里的一些方法結合起來使用的話,能提供極大的便利性,本節我們將會介紹到。

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

復制代碼代碼如下:

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()方法來從列表中過濾出了所有年齡大于20的人。然后,在forEach方法里,我們將元素添加到一個在前面已經初始化好的ArrayList中。我們先看下這段代碼的輸出結果,一會兒再去重構它。

復制代碼代碼如下:

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

 

程序輸出的結果是對的,不過還有點小問題。首先,把元素添加到集合中,這種屬于低級操作——它是命令式的,而非聲明式的。如果我們想把這個迭代改造成并發的,還得去考慮線程安全的問題——可變性使得它難以并行化。幸運的是,使用collect()方法可以很容易解決掉這個問題。來看下如何實現的。

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

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

對于串行操作而言,最后一條不是必需的;代碼設計的目標是能同時支持串行和并行的。

我們把這些操作提供給collect方法,讓它來把過濾后的流給收集起來。

 

復制代碼代碼如下:

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);

 

這段代碼的結果和前面一樣,不過這樣寫有諸多好處。

首先,我們編程的方式更聚焦了,表述性也更強,清晰的傳達了你要把結果收集到一個ArrayList里去的目的。collect()的第一個參數是一工廠或者生產者,后面的參數是一個用來收集元素的操作。

第二,由于我們沒有在代碼中個執行顯式的修改操作,可以很容易并行地執行這個迭代。我們讓底層庫來完成修改操作,它自己會處理好協作及線程安全的問題,盡管ArrayList本身不是線程安全的——干的漂亮。

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

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

復制代碼代碼如下:

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

 

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

我們來用下groupingBy()方法來將人群按年齡進行分組。

 

復制代碼代碼如下:

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

 

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

復制代碼代碼如下:

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

 

這些人已經按年齡進行了分組。

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

繼續使用上面這個例子,這回我們不按年齡分組了,我們只獲取人的名字,按他們的年齡進行排序。

 

復制代碼代碼如下:

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

 

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

 

復制代碼代碼如下:

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

 

可以看到,人們的名字已經按年齡進行分組了。

我們再來看一個組合的操作:按名字的首字母進行分組,然后選出每個分組中年紀最大的那位。

 

復制代碼代碼如下:

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);

 

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

 

復制代碼代碼如下:

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

 

我們已經領教到了collect()方法以及Collectors工具類的威力。在你的IDE或者JDK的官方文檔里面,再花點時間去研究下Collectors工具類吧,熟悉下它提供的各種方法。下面我們將會用lambda表達式來完成一些過濾器的實現。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日美av在线 | 偷偷草网站 | 久色免费视频 | 日本黄色大片免费 | japanese嫩21videos| 视频一区国产 | 日韩黄色免费电影 | 精品国产一区二区三区天美传媒 | 日本搞逼视频 | 天天干干| 久久久经典视频 | 蝌蚪久久窝 | 曰韩精品| 欧美福利视频一区二区三区 | 91成人精品 | 一起草av在线 | 黄色视频a级毛片 | 91视频观看 | 精品国产高清一区二区三区 | 31freehdxxxx欧美| 欧美激情精品久久久久久久久久 | 激情视频在线播放 | 中文字幕xxx | 激情视频日韩 | 精品国产一区二区三区四区阿崩 | 九九热免费精品视频 | 日本一区视频在线观看 | 久久久久国产一区二区三区不卡 | 国产精品99久久久久久宅女 | 91国在线高清视频 | 国产v综合v亚洲欧美久久 | 成年人毛片视频 | 草草视频免费 | 久久久久久久久久久久久久av | 色婷婷综合久久久中字幕精品久久 | 免费国产成人高清在线看软件 | 国产成人精品免费视频大全办公室 | 亚洲成人午夜精品 | 日韩精品dvd | 欧美成a人片在线观看久 | 99精品视频在线 |