例5灭抑、如何在lambda表達(dá)式中加入Predicate
上個(gè)例子說(shuō)到泉孩,java.util.function.Predicate 允許將兩個(gè)或更多的 Predicate 合成一個(gè)。它提供類似于邏輯操作符AND和OR的方法,名字叫做and()、or()和xor()善已,用于將傳入 filter() 方法的條件合并起來(lái)。例如离例,要得到所有以J開(kāi)始换团,長(zhǎng)度為四個(gè)字母的語(yǔ)言,可以定義兩個(gè)獨(dú)立的 Predicate 示例分別表示每一個(gè)條件宫蛆,然后用 Predicate.and() 方法將它們合并起來(lái)艘包,如下所示:
// 甚至可以用and()、or()和xor()邏輯函數(shù)來(lái)合并Predicate耀盗,
// 例如要找到所有以J開(kāi)始想虎,長(zhǎng)度為四個(gè)字母的名字,你可以合并兩個(gè)Predicate并傳入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
類似地叛拷,也可以使用 or() 和 xor() 方法磷醋。本例著重介紹了如下要點(diǎn):可按需要將 Predicate 作為單獨(dú)條件然后將其合并起來(lái)使用。簡(jiǎn)而言之胡诗,你可以以傳統(tǒng)Java命令方式使用 Predicate 接口,也可以充分利用lambda表達(dá)式達(dá)到事半功倍的效果淌友。
例6煌恢、Java 8中使用lambda表達(dá)式的Map和Reduce示例
本例介紹最廣為人知的函數(shù)式編程概念map。它允許你將對(duì)象進(jìn)行轉(zhuǎn)換震庭。例如在本例中瑰抵,我們將 costBeforeTax 列表的每個(gè)元素轉(zhuǎn)換成為稅后的值。我們將 x -> x*x lambda表達(dá)式傳到 map() 方法器联,后者將其應(yīng)用到流中的每一個(gè)元素二汛。然后用 forEach() 將列表元素打印出來(lái)婿崭。使用流API的收集器類,可以得到所有含稅的開(kāi)銷肴颊。有 toList() 這樣的方法將 map 或任何其他操作的結(jié)果合并起來(lái)氓栈。由于收集器在流上做終端操作,因此之后便不能重用流了婿着。你甚至可以用流API的 reduce() 方法將所有數(shù)字合成一個(gè)授瘦,下一個(gè)例子將會(huì)講到。
// 不使用lambda表達(dá)式為每個(gè)訂單加上12%的稅
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}
// 使用lambda表達(dá)式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
輸出:
112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0
例6.2竟宋、Java 8中使用lambda表達(dá)式的Map和Reduce示例
在上個(gè)例子中提完,可以看到map將集合類(例如列表)元素進(jìn)行轉(zhuǎn)換的。還有一個(gè) reduce() 函數(shù)可以將所有值合并成一個(gè)丘侠。Map和Reduce操作是函數(shù)式編程的核心操作徒欣,因?yàn)槠涔δ埽瑀educe 又被稱為折疊操作蜗字。另外打肝,reduce 并不是一個(gè)新的操作,你有可能已經(jīng)在使用它秽澳。SQL中類似 sum()闯睹、avg() 或者 count() 的聚集函數(shù),實(shí)際上就是 reduce 操作担神,因?yàn)樗鼈兘邮斩鄠€(gè)值并返回一個(gè)值楼吃。流API定義的 reduceh() 函數(shù)可以接受lambda表達(dá)式,并對(duì)所有值進(jìn)行合并妄讯。IntStream這樣的類有類似 average()孩锡、count()、sum() 的內(nèi)建方法來(lái)做 reduce 操作亥贸,也有mapToLong()躬窜、mapToDouble() 方法來(lái)做轉(zhuǎn)換。這并不會(huì)限制你炕置,你可以用內(nèi)建方法荣挨,也可以自己定義。在這個(gè)Java 8的Map Reduce示例里朴摊,我們首先對(duì)所有價(jià)格應(yīng)用 12% 的VAT默垄,然后用 reduce() 方法計(jì)算總和。
// 為每個(gè)訂單加上12%的稅
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
// 新方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
輸出:
Total : 1680.0
Total : 1680.0
例7甚纲、通過(guò)過(guò)濾創(chuàng)建一個(gè)String列表
過(guò)濾是Java開(kāi)發(fā)者在大規(guī)模集合上的一個(gè)常用操作口锭,而現(xiàn)在使用lambda表達(dá)式和流API過(guò)濾大規(guī)模數(shù)據(jù)集合是驚人的簡(jiǎn)單。流提供了一個(gè) filter() 方法介杆,接受一個(gè) Predicate 對(duì)象鹃操,即可以傳入一個(gè)lambda表達(dá)式作為過(guò)濾邏輯韭寸。下面的例子是用lambda表達(dá)式過(guò)濾Java集合,將幫助理解荆隘。
// 創(chuàng)建一個(gè)字符串列表恩伺,每個(gè)字符串長(zhǎng)度大于2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
輸出:
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]
另外,關(guān)于 filter() 方法有個(gè)常見(jiàn)誤解臭胜。在現(xiàn)實(shí)生活中莫其,做過(guò)濾的時(shí)候,通常會(huì)丟棄部分耸三,但使用filter()方法則是獲得一個(gè)新的列表乱陡,且其每個(gè)元素符合過(guò)濾原則。
例8仪壮、對(duì)列表的每個(gè)元素應(yīng)用函數(shù)
我們通常需要對(duì)列表的每個(gè)元素使用某個(gè)函數(shù)憨颠,例如逐一乘以某個(gè)數(shù)、除以某個(gè)數(shù)或者做其它操作积锅。這些操作都很適合用 map() 方法爽彤,可以將轉(zhuǎn)換邏輯以lambda表達(dá)式的形式放在 map() 方法里,就可以對(duì)集合的各個(gè)元素進(jìn)行轉(zhuǎn)換了缚陷,如下所示适篙。
// 將字符串換成大寫并用逗號(hào)鏈接起來(lái)
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
輸出:
USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA
例9、復(fù)制不同的值箫爷,創(chuàng)建一個(gè)子列表
本例展示了如何利用流的 distinct() 方法來(lái)對(duì)集合進(jìn)行去重嚷节。
// 用所有不同的數(shù)字創(chuàng)建一個(gè)正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s, Square Without duplicates : %s %n", numbers, distinct);
輸出:
Original List : [9, 10, 3, 4, 7, 3, 4], Square Without duplicates : [81, 100, 9, 16, 49]
例10、計(jì)算集合元素的最大值虎锚、最小值硫痰、總和以及平均值
IntStream、LongStream 和 DoubleStream 等流的類中窜护,有個(gè)非常有用的方法叫做 summaryStatistics() 效斑。可以返回 IntSummaryStatistics柱徙、LongSummaryStatistics 或者 DoubleSummaryStatistic s缓屠,描述流中元素的各種摘要數(shù)據(jù)。在本例中护侮,我們用這個(gè)方法來(lái)計(jì)算列表的最大值和最小值敌完。它也有 getSum() 和 getAverage() 方法來(lái)獲得列表的所有元素的總和及平均值。
//獲取數(shù)字的個(gè)數(shù)概行、最小值、最大值弧岳、總和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
輸出:
Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9
Lambda表達(dá)式 vs 匿名類
既然lambda表達(dá)式即將正式取代Java代碼中的匿名內(nèi)部類凳忙,那么有必要對(duì)二者做一個(gè)比較分析业踏。一個(gè)關(guān)鍵的不同點(diǎn)就是關(guān)鍵字 this。匿名類的 this 關(guān)鍵字指向匿名類涧卵,而lambda表達(dá)式的 this 關(guān)鍵字指向包圍lambda表達(dá)式的類勤家。另一個(gè)不同點(diǎn)是二者的編譯方式。Java編譯器將lambda表達(dá)式編譯成類的私有方法柳恐。使用了Java 7的 invokedynamic 字節(jié)碼指令來(lái)動(dòng)態(tài)綁定這個(gè)方法伐脖。