lambda表達(dá)式是Java 8 中的一個(gè)很重要的新特性,它容許將行為傳到函數(shù)中。在Java 8 之前贪磺,如果我們想要把行為傳到函數(shù)中,僅有的選擇就是匿名內(nèi)部類(lèi)诅愚。但Java 8 發(fā)布以后寒锚,lambda表達(dá)式將大量替代匿名內(nèi)部類(lèi)的使用,簡(jiǎn)化代碼违孝,突出匿名內(nèi)部類(lèi)中最重要的邏輯代碼刹前。下面以Runable
接口說(shuō)明這一點(diǎn):
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
使用lambda表達(dá)式簡(jiǎn)化:
Runnable runnable1 = () -> System.out.println(Thread.currentThread().getName());
一、lambda表達(dá)式簡(jiǎn)介
lambda表達(dá)式是一個(gè)匿名函數(shù)等浊,一種沒(méi)有聲明的方法腮郊,沒(méi)有修飾符,返回值聲明和名稱(chēng)筹燕。Java中的lambda表達(dá)式通常使用語(yǔ)法是(parameters) -> (expression)
或(parameters) ->{ statements; }
,例如:
// 接收兩個(gè)int型參數(shù)轧飞,返回它們的值
(int a, int b) -> { return a + b; }
// 無(wú)參數(shù)
() -> System.out.println("Hello World");
// 接收一個(gè)String型字符串衅鹿,打印字符串
(String s) -> { System.out.println(s); }
// 無(wú)參數(shù) 返回 42
() -> 42
// 無(wú)參數(shù),返回3.1415
() -> { return 3.1415 };
lambda表達(dá)式的結(jié)構(gòu)說(shuō)明:
- Lambda 表達(dá)式可以具有零個(gè)过咬,一個(gè)或多個(gè)參數(shù)大渤;
- 可以顯式聲明參數(shù)的類(lèi)型,也可以由編譯器自動(dòng)從上下文推斷參數(shù)的類(lèi)型掸绞。例如 (int a) 與剛才相同 (a)泵三;
- 參數(shù)用小括號(hào)括起來(lái),用逗號(hào)分隔衔掸。例如 (a, b) 或 (int a, int b) 或 (String a, int b, float c)烫幕;
- 空括號(hào)用于表示一組空的參數(shù)。例如 () -> 42敞映;
- 當(dāng)有且僅有一個(gè)參數(shù)時(shí)较曼,如果不顯式指明類(lèi)型,則不必使用小括號(hào)振愿。例如 a -> return a*a捷犹;
- Lambda 表達(dá)式的正文可以包含零條,一條或多條語(yǔ)句冕末;
- 如果 Lambda 表達(dá)式的正文只有一條語(yǔ)句萍歉,則大括號(hào)可不用寫(xiě),且表達(dá)式的返回值類(lèi)型要與匿名函數(shù)的返回類(lèi)型相同档桃;
- 如果 Lambda 表達(dá)式的正文有一條以上的語(yǔ)句必須包含在大括號(hào)(代碼塊)中枪孩,且表達(dá)式的返回值類(lèi)型要與匿名函數(shù)的返回類(lèi)型相同。
二胳蛮、lambda表達(dá)式示例
1. 使用Java 8 lambda表達(dá)式進(jìn)行事件處理
java 8 之前:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello world");
}
});
java 8 :
show.addActionListener((e) -> {
System.out.println("Hello world");
});
2. 使用Java 8 lambda表達(dá)式進(jìn)行列表迭代
java 8 之前:
List<String> items = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String item : items) {
System.out.println(item);
}
java 8 :
items.forEach(item -> System.out.println(item));
java 8 之前:
Map<String,String> map = new HashMap <>();
map.put("1","a");
map.put("2","b");
map.put("3","c");
for(String key : map.keySet()){
System.out.println(map.get(key));
}
java 8 :
map.forEach((k,v)->System.out.println(map.get(k)));
3. 使用lambda表達(dá)式和函數(shù)式接口Predicate進(jìn)行邏輯操作
Java 8 添加了一個(gè) java.util.function
的包销凑。它包含了很多類(lèi)丛晌,用來(lái)支持Java的函數(shù)式編程仅炊。其中一個(gè)便是Predicate,使用 java.util.function.Predicate
函數(shù)式接口以及l(fā)ambda表達(dá)式澎蛛,可以向API方法添加邏輯抚垄,用更少的代碼支持更多的動(dòng)態(tài)行為。
public static void main(String[] arg){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.print("輸出所有數(shù)字:");
evaluate(list, (n) -> true);
System.out.print("不輸出:");
evaluate(list, (n) -> false);
System.out.print("輸出偶數(shù):");
evaluate(list, (n) -> n % 2 == 0);
System.out.print("輸出奇數(shù):");
evaluate(list, (n) -> n % 2 == 1);
System.out.print("輸出大于 5 的數(shù)字:");
evaluate(list, (n) -> n > 5);
}
public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
// for (Integer n : list) {
// if (predicate.test(n)) {
// System.out.print(n + " ");
// }
// }
list.stream().filter(name -> predicate.test(name)).forEach(name -> System.out.println(name+" "));
}
4. 多個(gè)Predicate合并使用
java.util.function.Predicate
允許將兩個(gè)或更多的 Predicate 合成一個(gè)谋逻。它提供類(lèi)似于邏輯操作符AND和OR的方法呆馁,名字叫做and()、or()和xor()毁兆,用于將傳入 filter() 方法的條件合并起來(lái)浙滤。例如,要得到所有以J開(kāi)始气堕,長(zhǎng)度為四個(gè)字母的語(yǔ)言纺腊,可以定義兩個(gè)獨(dú)立的 Predicate 示例分別表示每一個(gè)條件畔咧,然后用 Predicate.and()
方法將它們合并起來(lái),如下所示:
List<String> list = Arrays.asList("Java", "C", "C++", "Ruby", "C#", "Go");
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
list.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
5. Java 8中使用lambda表達(dá)式的Map和Reduce
java.util.stream.Stream
接口 和 Lambda 表達(dá)式一樣揖膜,都是 Java 8 新引入的誓沸。所有 Stream 的操作必須以 Lambda 表達(dá)式為參數(shù)。Stream 接口中帶有大量有用的方法壹粟,比如 map() 的作用就是將 input Stream 的每個(gè)元素映射成output Stream 的另外一個(gè)元素拜隧。
下面的例子,我們將 Lambda 表達(dá)式 x -> x*x
傳遞給map()
方法趁仙,將其應(yīng)用于流的所有元素洪添。之后,我們使用forEach
打印列表的所有元素:
java 8 之前:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
int x = n * n;
System.out.println(x);
}
java 8 :
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
下面的示例中雀费,我們給定一個(gè)列表薇组,然后求列表中每個(gè)元素的平方和。這個(gè)例子中坐儿,我們使用了 reduce() 方法律胀,這個(gè)方法的主要作用是把 Stream 元素組合起來(lái):
java 8 之前:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
int x = n * n;
sum = sum + x;
}
System.out.println(sum);
java 8 :
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);
6. 通過(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集合黑低,將幫助理解。
List<String> strList = Arrays.asList("Java", "C", "C++", "Ruby", "C#", "Go");
// 創(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);
7. 去重操作
本例展示了如何利用流的 distinct() 方法來(lái)對(duì)集合進(jìn)行去重:
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);
8. 計(jì)算集合元素的最大值克握、最小值、總和以及平均值
IntStream枷踏、LongStream 和 DoubleStream 等流的類(lèi)中菩暗,有個(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());