Lambda簡介
Lambda表達式是java8的特性(現在已經14了我就把“新”去了哈),主要依靠操作符"->"來讓符合 輸入( x , y ) -> {輸出 x 與 y 運算後的結果} 的代碼能夠更加的簡潔與靈活。
使用Lambda表達式需要聲明一個函數式接口(有且僅有一個抽象方法的接口)。
比如,我們自己定義一個求和的函數式接口並用Lambda表達式進行運算:
//定義一個接口
public interface MyAdd {
public int add(int a,int b);
}
//定義測試類
public class AddTest01 {
public static void main(String[] args) {
//寫法是很智能的,因爲接口的參數是int型,所以省略數據類型是可以的
MyAdd add1 = (a,b) -> a + b;
//因爲返回值類型是int,所以不加return也認
MyAdd add2 = (int a,int b) -> a + b;
//最全乎的格式
MyAdd add3 = (int a,int b) -> {return a + b;};
System.out.println(add1.add(1, 2));
System.out.println(add2.add(1, 2));
System.out.println(add3.add(1, 2));
}
}
運算結果如下
3
3
3
除了我們自己定義的接口以外,這個Lambda也可以很好的改善我們原先的一些代碼。比如之前我們把Student對象存入TreeSet時,如果Student沒有實現Comparable並且在new TreeSet的時候沒有傳入比較器的話,就會報錯java.lang.ClassCastException: day01.Student cannot be cast to java.lang.Comparable所以我們會通過匿名內部類的方式傳入一個比較器,代碼如下:
//此處省略Student類以及new學生的代碼了
...
//創建TreeSet
Set<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2){
return s1.getAge() - s2.getAge();
}
});
這個時候我們再看看用Lambda表達式如何改寫
//創建TreeSet
Set<Student> set = new TreeSet<>((stu1,stu2) -> s1.getAge() - s2.getAge());
沒錯,這就完了,對比一下就能發現其實就是根據年齡作比較,很簡單的一個比較邏輯還要new個比較器再重寫方法寫這麼一套是不是有些麻煩,這樣一看Lambda確實有它存在的合理性了對吧?
Stream簡介
Stream流操作也是java8加進來的,是基於上面的函數式編程的在集合類上進行復雜操作的工具。之前都需要通過寫遍歷或者寫迭代器相關代碼對於每個元素進行篩選,但運用Stream就可以只傳入規則,剩下的交給集合類去做,我們坐等結果就好了。
我的理解
- Stream的操作分爲兩大類:中間操作 終止操作
- 概念區分: 最直接的就是看這個方法結束之後返回地還是不是Stream。就像是大盤chicken,先抓個chicken(獲取數據源),然後進行中間操作拔毛(distinct),按大小個擺盤(sorted)等等,不管咱們在後廚拔毛還是稱重或者幹啥,這個chicken還是chicken,還沒有端上桌你就還有曹作的餘地,但是隻要上桌了(collect等終止操作),你就別想再拔個毛再給人換個小點的chicken幹啥了,一切都結束了,晚矣。
Stream實例記錄(上代碼)
中間操作
排序(sorted):先通過list.stream()獲取數據源,然後調用sorted方法進行排序(需要元素實現了Comparable),最後使用終止操作collect(Collector<? super T, A, R> collector)再次轉換爲集合,裏面參數看不懂,不過java8提供了Collectors來簡化,轉list就如代碼所示,轉Map也有其對應的方法。
//獲取一個集合
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(3);
list1.add(7);
list1.add(5);
list1.add(5);
list1.add(6);
//獲得排序過的list
List<Integer> newList = list1.stream().sorted().collect(Collectors.toList());
//輸出
System.out.println(newList);
執行結果:
[1, 3, 5, 5, 6, 7]
後面的例子都是先取數據源,最後轉換回list的時候,(stream,collect)這兩個方法就不再重複解說了哈。
篩選(filter):調用filter()方法來保留符合規則的元素,需要傳入Predicate對象,重寫其test方法,返回true的元素留下。我們現在只要能被3整除的元素。示例集合:[1, 3, 5, 5, 6, 7]
//獲得符合規則元素的list
List<Integer> newList = list1.stream().filter(new Predicate<Integer>() {
@Override
public boolean test(Integer t) {
return t%3 == 0;
}
執行結果:
[3, 6]
去重(distinct):調用distinct()方法去掉重複項,類似SQL。示例集合:[1, 3, 5, 5, 6, 7]
//獲得去重過的list
List<Integer> newList = list1.stream().distinct().collect(Collectors.toList());
執行結果:
[1, 3, 7, 5, 6]
映射(map): 調用map,使所有元素根據給定的規則生成新的一個數據流。比如我要原list所有元素的值翻一倍。示例集合:[1, 3, 5, 5, 6, 7]
//獲得翻倍過的list
List<Integer> newList = list1.stream().map((num1) -> num1*2).collect(Collectors.toList());
執行結果:
[2, 6, 14, 10, 12, 10]
終止操作
最終解(reduce):調用reduce ,將這個數據流根據運算返回一個最終結果,放入結果容器(Optional)中,不過這個方法有三種形式,分別演示一下,個人認爲這也是終止操作裏最難的嘞
一個參數:reduce(BinaryOperator accumulator) 返回最大的一個值。示例集合:[1, 3, 5, 5, 6, 7]
//獲得最大值
Optional<Integer> result = list1.stream().reduce((num1,num2)->num1 > num2?num1:num2);
執行結果:
Optional[7]
兩個參數:reduce(T identity, BinaryOperator accumulator) 返回最大的一個值,不過他會讓第一個參數也參入運算。示例集合:[1, 3, 5, 5, 6, 7]
//獲得最大值,999也參加運算
Integer result = list1.stream().reduce(999,(num1,num2)->num1 > num2?num1:num2);
執行結果:
999
參數不同發現返回值也直接成了一個Integer而不再是Optional對象
三個參數:有點高深,還牽扯到並行,正在學習,大家也可以在評論裏討論一下。
還有:
collect(收集): 上面每個例子都用到了,大家應該也能感受了,第一個例子也進行了說明。
max、min、anyMatch、allMatch 、findAny、findFirs、skip、foreach這些根據字面意思大家也應該可以猜到了,求最大,求最小,有一個滿足,全部滿足,找到任意一個,找第一個,跳過前幾個元素,遍歷等等,這些大家調一次打印一下應該都就知道是幹嘛的了,只要返回的還是Stream你發現還能接着點出stream的其他操作,就是中間操作。
最後說一下,多箇中間操作組合起來就是一個大的中間操作,只要沒有調用終止操作中的任意一個,就一直可以繼續下去。比如,先去重,再排序,再每個翻個倍,最後遍歷輸出。示例集合:[1, 3, 5, 5, 6, 7]
//先去重,再排序,再每個翻個倍,最後遍歷輸出
list1.stream().distinct().sorted().map(num1 -> num1*2).forEach(System.out::println);
如果覺得那裏寫的不對可以評論或者私信我,一起在激烈的討論中擦出奇怪的火花。