引用來源
https://www.cnblogs.com/snowInPluto/p/5981400.html
https://my.oschina.net/joshuashaw/blog/487322
https://blog.csdn.net/yangjiachang1203/article/details/52619795
https://www.cnblogs.com/invoker-/p/7709052.html
Lambda表達式
java 8引入了Lambda表達式随闪,用于簡化代碼編寫。
- demo
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("button clicked");
}
});
上面這段代碼骚勘,使用lambda表達式 可以簡化為:
button.addActionListener(event -> System.out.println("button clicked"));
- Lambda表達式的五種形式
2.1 不包含參數(shù),使用空括號 () 表示沒有參數(shù)铐伴。例如撮奏,接口Runnable只有一個方法run。
Runnable noArguments = () -> System.out.println("Hello World");
該 Lambda 表達式 實現(xiàn)了 Runnable 接口,該接口也只有一個 run 方法,沒有參數(shù),且返回類型為 void当宴。
2.2 包含且只包含一個參數(shù),可省略參數(shù)的括號
ActionListener oneArgument = event -> System.out.println("button clicked");
2.3 Lambda 表達式的主體不僅可以是一個表達式,而且也可以是一段代碼塊,使用大括號 ({})將代碼塊括起來畜吊。該代碼塊和普通方法遵循的規(guī)則別無二致,可以用返 回或拋出異常來退出。只有一行代碼的 Lambda 表達式也可使用大括號,用以明確 Lambda表達式從何處開始户矢、到哪里結(jié)束玲献。
Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};
2.4 Lambda 表達式也可以表示包含多個參數(shù)的方法,如?所示。這時就有必要思考怎樣去閱 讀該 Lambda 表達式逗嫡。這行代碼并不是將兩個數(shù)字相加,而是創(chuàng)建了一個函數(shù),用來計算 兩個數(shù)字相加的結(jié)果青自。變量 add 的類型是 BinaryOperator。
BinaryOperator<Long> add = (x, y) -> x + y;
2.5 Lambda 表達式都可以擴寫為原始的“匿名類”形式驱证。當你覺得這個 Lambda 表達式很復雜不容易理解的時候延窜,不妨把它擴寫為“匿名類”形式來看。
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;
函數(shù)接口
- 定義
所謂的函數(shù)式接口抹锄,當然首先是一個接口逆瑞,然后就是在這個接口里面只能有一個抽象方法。這種類型的接口也稱為SAM接口伙单,即Single Abstract Method interfaces获高。- 函數(shù)式接口里是可以包含默認方法,因為默認方法不是抽象方法吻育,其有一個默認實現(xiàn)念秧,所以是符合函數(shù)式接口的定義的;
- 函數(shù)式接口里是可以包含靜態(tài)方法布疼,因為靜態(tài)方法不能是抽象方法摊趾,是一個已經(jīng)實現(xiàn)了的方法,所以是符合函數(shù)式接口的定義的游两;
- 函數(shù)式接口里是可以包含Object里的public方法砾层,這些方法對于函數(shù)式接口來說,不被當成是抽象方法(雖然它們是抽象方法)贱案;因為任何一個函數(shù)式接口的實現(xiàn)肛炮,默認都繼承了Object類,包含了來自java.lang.Object里對這些抽象方法的實現(xiàn)宝踪;
- 函數(shù)式接口里允許子接口繼承多個父接口侨糟,但每個父接口中都只能存在一個抽象方法,且必須的相同的抽象方法瘩燥。
- 常用的核心函數(shù)接口
- Predicate<T>粟害,參數(shù):T,返回boolean颤芬。用于判別一個對象悲幅。比如求一個人是否為男性
- Consumer<T> 套鹅,參數(shù)T,返回void汰具。用于接收一個對象進行處理但沒有返回卓鹿,比如接收一個人并打印他的名字
- Function<T, R> ,參數(shù)T留荔,返回R吟孙。用于轉(zhuǎn)換一個對象為不同類型的對象。
- Supplier<T> 聚蝶,無參數(shù)杰妓。返回T。用于提供一個對象碘勉。
- UnaryOperator<T>巷挥,參數(shù)T,返回T验靡。用于接收對象并返回同類型的對象倍宾。
- BinaryOperator<T>,參數(shù)(T, T)胜嗓,返回T高职。用于接收兩個同類型的對象,并返回一個原類型對象辞州。
- 其中 Cosumer 與 Supplier 對應(yīng)屏镊,一個是消費者炕置,一個是提供者劝枣。Predicate 用于判斷對象是否符合某個條件明棍,經(jīng)常被用來過濾對象。Function 是將一個對象轉(zhuǎn)換為另一個對象牵啦,比如說要裝箱或者拆箱某個對象。UnaryOperator 接收和返回同類型對象妄痪,一般用于對對象修改屬性哈雏。BinaryOperator 則可以理解為合并對象。
Stream操作
- Java 8 中衫生,引入了流(Stream)的概念裳瘪,這個流和以前我們使用的 IO 中的流并不太相同。所有繼承自 Collection 的接口都可以轉(zhuǎn)換為 Stream罪针。demo:
class Person {
public String name;
public int age;
}
List<Person> personList;
統(tǒng)計personList中年齡大于20的數(shù)目:
long count = personList.stream()
.filter(person -> person.getAge() > 20)
.count();
- Stream 常用操作彭羹。Stream 的方法分為兩類。一類叫惰性求值泪酱,一類叫及早求值派殷。判斷一個操作是惰性求值還是及早求值很簡單:只需看它的返回值还最。如果返回值是 Stream,那么是惰性求值毡惜。其實可以這么理解拓轻,如果調(diào)用惰性求值方法,Stream 只是記錄下了這個惰性求值方法的過程经伙,并沒有去計算扶叉,等到調(diào)用及早求值方法后,就連同前面的一系列惰性求值方法順序進行計算帕膜,返回結(jié)果枣氧。通用形式為:
Stream.惰性求值.惰性求值. ... .惰性求值.及早求值
- collect(toList())。由 Stream 里的值生成一個列表垮刹,是一個及早求值操作达吞。
List<String> collected = Stream.of("a", "b", "c")
.collect(Collectors.toList());
- map. 如果有一個函數(shù)可以將一種類型的值轉(zhuǎn)換成另外一種類型,map 操作就可以使用該函數(shù)危纫,將一個流中的值轉(zhuǎn)換成一個新的流宗挥。map 方法就是接受的一個 Function 的匿名函數(shù)類,進行的轉(zhuǎn)換种蝶。
List<String> collected = Stream.of("a", "b", "hello")
.map(string -> string.toUpperCase())
.collect(toList());
- filter契耿。遍歷數(shù)據(jù)并檢查其中的元素時,可嘗試使用 Stream 中提供的新方法 filter螃征。filter 方法就是接受的一個 Predicate 的匿名函數(shù)類搪桂,判斷對象是否符合條件,符合條件的才保留下來盯滚。
List<String> beginningWithNumbers =
Stream.of("a", "1abc", "abc1")
.filter(value -> isDigit(value.charAt(0)))
.collect(toList());
- flatMap. 可用 Stream 替換值踢械,然后將多個 Stream 連接成一個 Stream。flatMap 最常用的操作就是合并多個 Collection魄藕。
List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
.flatMap(numbers -> numbers.stream())
.collect(toList());
- max和min. Stream 上常用的操作之一是求最大值和最小值内列。Stream API 中的 max 和 min 操作足以解決這一問題。max 和 min 方法返回的是一個 Optional 對象.
List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);
int maxInt = list.stream()
.max(Integer::compareTo)
.get();
int minInt = list.stream()
.min(Integer::compareTo)
.get();
Integer::compareTo 也是屬于 Java 8 引入的新特性背率,叫做 方法引用(Method References)话瞧。在這邊,其實就是 (int1, int2) -> int1.compareTo(int2) 的簡寫
- reduce. reduce 操作可以實現(xiàn)從一組值中生成一個值寝姿。上述例子中用到的 count交排、min 和 max 方法,因為常用而被納入標準庫中。事實上饵筑,這些方法都是 reduce 操作埃篓。reduce 的第一個參數(shù),是一個初始值根资。
int result = Stream.of(1, 2, 3, 4)
.reduce(0, (acc, element) -> acc + element);
int result = Stream.of(1, 2, 3, 4)
.reduce(1, (acc, element) -> acc * element);
- 數(shù)據(jù)并行化操作架专。并行化操作流只需改變一個方法調(diào)用同窘。如果已經(jīng)有一個 Stream 對象,調(diào)用它的 parallel() 方法就能讓其擁有并行操作的能力胶征。如果想從一個集合類創(chuàng)建一個流塞椎,調(diào)用 parallelStream() 就能立即獲得一個擁有并行能力的流。
int sumSize = Stream.of("Apple", "Banana", "Orange", "Pear")
.parallel()
.map(s -> s.length())
.reduce(Integer::sum)
.get();
如果你去計算這段代碼所花的時間睛低,很可能比不加上 parallel() 方法花的時間更長案狠。這是因為數(shù)據(jù)并行化會先對數(shù)據(jù)進行分塊,然后對每塊數(shù)據(jù)開辟線程進行運算钱雷,這些地方會花費額外的時間骂铁。并行化操作只有在 數(shù)據(jù)規(guī)模比較大 或者 數(shù)據(jù)的處理時間比較長 的時候才能體現(xiàn)出優(yōu)勢,所以并不是每個地方都需要讓數(shù)據(jù)并行化罩抗,應(yīng)該具體問題具體分析拉庵。
- 收集器collect。Stream 轉(zhuǎn)換為 List 是很常用的操作套蒂,其他 Collectors 還有很多方法钞支,可以將 Stream 轉(zhuǎn)換為 Set, 或者將數(shù)據(jù)分組并轉(zhuǎn)換為 Map,并對數(shù)據(jù)進行處理操刀。也可以指定轉(zhuǎn)換為具體類型烁挟,如 ArrayList, LinkedList 或者 HashMap。甚至可以自定義 Collectors骨坑,編寫自己的收集器撼嗓。
- 元素順序。一些集合類型中的元素是按順序排列的欢唾,比如 List且警;而另一些則是無序的,比如 HashSet礁遣。增加了流操作后斑芜,順序問題變得更加復雜。如果集合本身就是無序的祟霍,由此生成的流也是無序的杏头。一些中間操作會產(chǎn)生順序,比如對值做映射時浅碾,映射后的值是有序的,這種順序就會保留 下來续语。如果進來的流是無序的垂谢,出去的流也是無序的。如果我們需要對流中的數(shù)據(jù)進行排序疮茄,可以調(diào)用 sorted 方法滥朱。
List<Integer> list = Lists.newArrayList(3, 5, 1, 10, 8);
List<Integer> sortedList = list.stream()
.sorted(Integer::compareTo)
.collect(Collectors.toList());
- @FunctionalInterface. 每個用作函數(shù)接口的接口都應(yīng)該添加這個注釋根暑。但 Java 中有一些接口,雖然只含一個方法徙邻,但并不是為了使用 Lambda 表達式來實現(xiàn)的排嫌。比如,有些對象內(nèi)部可能保存著某種狀態(tài)缰犁,使用帶有一個方法的接口可能純屬巧合淳地。該注釋會強制 javac 檢查一個接口是否符合函數(shù)接口的標準。如果該注釋添加給一個枚舉類型帅容、類或另一個注釋颇象,或者接口包含不止一個抽象方法,javac 就會報錯并徘。重構(gòu)代碼時遣钳,使用它能很容易發(fā)現(xiàn)問題。
實際使用
- 場景描述:現(xiàn)在有很多用戶麦乞,每個用戶使用userId來標識蕴茴,每個用戶都有一定數(shù)量的money。Person類的定義如下:
@Data
class Person {
private String userId;
private Integer money;
}
現(xiàn)在有一個List<Person> personList姐直,如果需要計算所有person的money總和倦淀,使用Lambda表達式,可以是:
Integer sum = personList.stream().mapToInt(p->p.getMoney()).sum();
- 場景描述:現(xiàn)在有一個List<String> stringList简肴,要求將所有的String晃听,按照長度分類。使用Lambda表達式砰识,可以是:
Map<Integer,List<String>> result = stringList.stream().collect(Collectors.groupingBy(s -> s.length()));