Java函數(shù)式編程(四):在集合中查找元素
查找元素
現(xiàn)在我們對(duì)這個(gè)設(shè)計(jì)優(yōu)雅的轉(zhuǎn)化集合的方法已經(jīng)不陌生了,但它對(duì)查找元素卻也是無(wú)能為力。不過(guò)filter方法卻是為這個(gè)而生的。
我們現(xiàn)在要從一個(gè)名字列表中,取出那些以N開(kāi)頭的名字。當(dāng)然可能一個(gè)也沒(méi)有,結(jié)果可能是個(gè)空集合。我們先用老方法實(shí)現(xiàn)一把。
final List<String> startsWithN = new ArrayList<String>();
for(String name : friends) {
if(name.startsWith("N")) {
startsWithN.add(name);
}
}
這么簡(jiǎn)單的事件,寫了這么多代碼,也夠啰嗦的了。我們先創(chuàng)建了一個(gè)變量,然后把它初始為一個(gè)空集合。然后遍歷原來(lái)的集合,查找那些以指定字母開(kāi)頭的名字。如果找到,就插入到集合里。
我們用filter方法來(lái)重構(gòu)一下上面這段代碼,看看它的威力到底如何。
final List<String> startsWithN =
friends.stream()
.filter(name -> name.startsWith("N"))
.collect(Collectors.toList());
filter方法接收一個(gè)返回布爾值的lambda表達(dá)式。如果表達(dá)式結(jié)果為true,運(yùn)行上下文中的那個(gè)元素就會(huì)被添加到結(jié)果集中;如果不是,就跳過(guò)它。最終返回的是一個(gè)Steam,它里面只包含那些表達(dá)式返回true的元素。最后我們用一個(gè)collect方法把這個(gè)集合轉(zhuǎn)化成一個(gè)列表——在后面52頁(yè)的使用collect方法和Collecters類中,我們會(huì)對(duì)這個(gè)方法進(jìn)去更深入的探討。
我們來(lái)打印一下這個(gè)結(jié)果集中的元素:
System.out.println(String.format("Found %d names", startsWithN.size()));
從輸出結(jié)果很明顯能看出來(lái),這個(gè)方法把集合中匹配的元素全都找出來(lái)了。
Found 2 names
filter方法和map方法一樣,也返回了一個(gè)迭代器,不過(guò)它們也就這點(diǎn)相同而已了。map返回的集合和輸入集合大小是一樣的,而filter返回的可不好說(shuō)。它返回的集合的大小區(qū)間,從0一直到輸入集的元素個(gè)數(shù)。和map不一樣的是,filter返回的是輸入集的一個(gè)子集。
到現(xiàn)在為止,lambda表達(dá)式帶來(lái)的代碼簡(jiǎn)潔性讓我們很滿意,不過(guò)如果不注意的話,代碼冗余的問(wèn)題就開(kāi)始慢慢滋長(zhǎng)了。下面我們來(lái)討論下這個(gè)問(wèn)題。
lambda表達(dá)式的重用
lambda表達(dá)式看起來(lái)很簡(jiǎn)潔,實(shí)際上一不小心很容易就出現(xiàn)代碼冗余了。冗余會(huì)導(dǎo)致代碼質(zhì)量低下,難以維護(hù);如果我們想做一個(gè)改動(dòng),得把好幾處相關(guān)的代碼都一起改掉才行。
避免冗余還可以幫忙我們提升性能。相關(guān)的代碼都集中在一個(gè)地方,這樣我們分析它的性能表現(xiàn),然后優(yōu)化這一處的代碼,很容易就能提升代碼的性能。
現(xiàn)在我們來(lái)看下為什么使用lambda表達(dá)式容易導(dǎo)致代碼冗余,同時(shí)考慮如何去避免它。
final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
final List<String> editors =
Arrays.asList("Brian", "Jackie", "John", "Mike");
final List<String> comrades =
Arrays.asList("Kate", "Ken", "Nick", "Paula", "Zach");
We want to filter out names that start with a certain letter.
我們希望過(guò)濾一下某個(gè)字母開(kāi)頭的名字。先用filter方法簡(jiǎn)單地實(shí)現(xiàn)一下。
final long countFriendsStartN =
friends.stream()
.filter(name -> name.startsWith("N")).count();
final long countEditorsStartN =
editors.stream()
.filter(name -> name.startsWith("N")).count();
final long countComradesStartN =
comrades.stream()
.filter(name -> name.startsWith("N")).count();
lambda表達(dá)式讓代碼看起來(lái)很簡(jiǎn)潔,不過(guò)它不知不覺(jué)的帶來(lái)了代碼的冗余。在上面這個(gè)例子中,如果想改一下lambda表達(dá)式,我們得改不止一處地方——這可不行。幸運(yùn)的是,我們可以把lambda表達(dá)式賦值給變量,然后對(duì)它們進(jìn)行重用,就像使用對(duì)象一樣。
filter方法,lambda表達(dá)式的接收方,接收的是一個(gè)java.util.function.Predicate函數(shù)式接口的引用。在這里,Java編譯器又派上用場(chǎng)了,它用指定的lambda表達(dá)式生成了Predicate的test方法的一個(gè)實(shí)現(xiàn)?,F(xiàn)在我們可以更明確的讓Java編譯器去生成這個(gè)方法,而不是在參數(shù)定義的地方再生成。在上面例子中,我們可以明確的把lambda表達(dá)式存儲(chǔ)到一個(gè)Predicate類型的引用里面,然后再把這個(gè)引用傳遞給filter方法;這樣做很容易就避免了代碼冗余。
我們來(lái)重構(gòu)前面這段代碼,讓它符合DRY的原則吧。(Don't Repeat Yoursef——DRY——原則,可以參看The Pragmatic Programmer: From Journeyman to Master[HT00],一書)。
final Predicate<String> startsWithN = name -> name.startsWith("N");
final long countFriendsStartN =
friends.stream()
.filter(startsWithN)
.count();
final long countEditorsStartN =
editors.stream()
.filter(startsWithN)
.count();
final long countComradesStartN =
comrades.stream()
.filter(startsWithN)
.count();
現(xiàn)在不用再重復(fù)寫那個(gè)lambda表達(dá)式了,我們只寫了一次,并把它存儲(chǔ)到了一個(gè)叫startsWithN的Predicate類型的引用里面。這后面的三個(gè)filter調(diào)用里,Java編譯器看到在Predicate偽裝下的lambda表達(dá)式,笑而不語(yǔ),默默接收了。
這個(gè)新引入的變量為我們消除了代碼冗余。不過(guò)不幸的是,后面我們就會(huì)看到,敵人很快又回來(lái)報(bào)仇雪恨了,我們?cè)倏纯从惺裁锤鼌柡Φ奈淦髂芴嫖覀兿麥缢鼈儭?/p>
相關(guān)文章
JDBC實(shí)現(xiàn)Mysql自動(dòng)重連機(jī)制的方法詳解
最近在工作中發(fā)現(xiàn)了一個(gè)問(wèn)題,通過(guò)查找相關(guān)的資料終于解決了,下面這篇文章主要給大家介紹了關(guān)于JDBC實(shí)現(xiàn)Mysql自動(dòng)重連機(jī)制的相關(guān)資料,文中給出多種解決的方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-07-07Java實(shí)現(xiàn)給Word文件添加文字水印
Word中設(shè)置水印時(shí),可預(yù)設(shè)的文字或自定義文字設(shè)置為水印效果,但通常添加水印效果時(shí),會(huì)對(duì)所有頁(yè)面都設(shè)置成統(tǒng)一效果。本文將利用Java給Word每一頁(yè)設(shè)置不同文字水印效果,需要的可以參考一下2022-02-02SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow)
這篇文章主要介紹了SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07解決SpringBoot文件上傳臨時(shí)目錄找不到的問(wèn)題
這篇文章主要介紹了解決SpringBoot文件上傳臨時(shí)目錄找不到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java基于IO流實(shí)現(xiàn)登錄和注冊(cè)功能
這篇文章主要為大家詳細(xì)介紹了Java基于IO流實(shí)現(xiàn)登錄和注冊(cè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04微信小程序微信登錄的實(shí)現(xiàn)方法詳解(JAVA后臺(tái))
通常我們?cè)诘卿浳⑿判〕绦虻臅r(shí)候都是通過(guò)授權(quán)登錄,下面這篇文章主要給大家介紹了關(guān)于微信小程序微信登錄的實(shí)現(xiàn)方法,文中通過(guò)實(shí)例代碼介紹的介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07Java創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)
當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個(gè)簡(jiǎn)單的對(duì)象,而是多個(gè)位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時(shí)需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)2022-09-09jdk17+springboot使用webservice的踩坑實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于jdk17+springboot使用webservice踩坑的相關(guān)資料,網(wǎng)上很多教程是基于jdk8的,所以很多在17上面跑不起來(lái),折騰兩天,直接給答案,需要的朋友可以參考下2024-01-01探索分析Redis?AOF日志與數(shù)據(jù)持久性
這篇文章主要為大家介紹了探索分析Redis?AOF日志與數(shù)據(jù)持久性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12