jdk1.8特性——lambda表示式、stream學習,結合使用
最近再專案中用到了lambda和Stream,發現用起來程式碼很簡潔,就是有些複雜點的可能用完後可讀性不是很理想,但是簡單點的還是很好理解的,因此專門試了試,感覺真的很棒~先來了解一下
一:lambda表示式
lambda語法:
1.多引數
(1). lambda表示式的基本格式為(x1,x2)->{表示式...};
(2). 在上式中,lambda表示式帶有兩個引數,此時引數型別可以省略,但兩邊的括號不能省略
(3). 如果表示式只有一行,那麼表示式兩邊的花括號可以省略
public static void test1_() { List<User> usersList = new ArrayList<User>() { { add(new User("user1", "stu1", 10)); add(new User("user2", "stu2", 6)); add(new User("user3", "stu3", 8)); add(new User("user4", "stu4", 7)); } }; Collections.sort(usersList, (s1, s2) -> Integer.compare(s1.getAge(), s2.getAge())); System.out.println(usersList); }
2.對於沒有引數的情況 :
(1).引數的括號不能省略,
(2).其他語法同多引數
如:無lambda
public static void testThread(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello, testThread !");
}
}).start();
}
如:通過lambda寫法
public static void testThreadLambda(){ new Thread(()-> System.out.println("hello, testThreadLambda!")).start(); }
一:stream()
- Stream是元素的集合,這點讓Stream看起來用些類似Iterator;
- 可以支援順序和並行的對原Stream進行匯聚的操作;
可以認為Stream是一個高階版本的iterator,原始版本的iterator,使用者只能一個一個的遍歷元素並對其執行某些操作,而stream,只要給出需要對其包含的元素執行什麼操作,比如“獲取每個字串的首字母”,“將每個字串的字母改為大寫”等,具體的這些操作如何應用到每個元素上,交給stream就好了。
List<Integer> nums = new ArrayList<Integer>(){ { add(1); add(null); add(2); add(3); add(null); } }; Long numCount = nums.stream().filter(num -> num != null).count(); System.out.println("numCount="+numCount);
借用別人的圖來分析一下~哈哈
可以看到紅色區域是Strean的生命開始的地方,負責建立一個Stream例項;綠色框中的語句是賦予Stream靈魂的地方,把一個Stream轉換成另一個Stream,紅框中的語句生成的是包含所有nums變數的Stream,經過綠框的filter以後,重新生成了一個過濾掉原nums列表所有null以後的Stream, 藍色框中的語句是把Stream的裡面包含的內容按照某種演算法來匯聚成一個值,例子中是獲取Stream中包含的元素個數。
總結一下使用Stream的基本步驟:
- 建立Stream。
- 轉換Stream,每次轉換原有Stream物件不改變,返回的是一個新的Stream物件(可以有多次轉換)。
- 對Stream進行聚合操作,獲取想要的結果,也可進行集合轉換,得到想要的集合結果。
建立Stream
建立Stream,常用的兩種方式,1通過Stream介面的靜態工廠方法;2通過Collection介面的預設方法–stream(),把一個Collection物件轉換成Stream。
轉化Stream
轉換Stream其實就是把一個Stream通過某些行為轉換成一個新的Stream。Stream介面中定義了幾個常用的轉換方法:
1. distinct: 對於Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重複的元素;
2. filter: 對於Stream中包含的元素使用給定的過濾函式進行過濾操作,新生成的Stream只包含符合條件的元素;
3.map: 對於Stream中包含的元素使用給定的轉換函式進行轉換操作,新生成的Stream只包含轉換生成的元素。這個方法有三個對於原始型別的變種方法,分別是:mapToInt,mapToLong和mapToDouble。這三個方法也比較好理解,比如mapToInt就是把原始Stream轉換成一個新的Stream,這個新生成的Stream中的元素都是int型別。之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗;
4.flatMap:和map類似,不同的是其每個元素轉換得到的是Stream物件,會把子Stream中的元素壓縮到父集合中;
5.peek: 生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函式(Consumer例項),新Stream每個元素被消費的時候都會執行給定的消費函式;
6. limit: 對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小於N,那就獲取其所有的元素;
7. skip: 返回一個丟棄原Stream的前N個元素後剩下元素組成的新Stream,如果原Stream中包含的元素個數小於N,那麼返回空Stream;
走一波試試:
List<Integer> nums = new ArrayList<Integer>(){
{
add(1);
add(null);
add(2);
add(3);
add(null);
add(5);
add(3);
add(3);
add(null);
add(5);
}
};
System.out.println("sum is:"+nums.stream().filter(num -> num != null).distinct().mapToInt(num -> num *2).peek(System.out::println).skip(2).limit(4).sum());
這波先將nums進行非空過濾,留下所有非空元素,然後對於留下非空元素進行去重,然後再每個元素乘以2,再每個元素被消費的時候列印自身,在跳過前兩個元素,最後去前四個元素進行加和運算。當然啦,前四個元素不夠就有幾個算幾個啦。
效能問題:
有些細心的同學可能會有這樣的疑問:在對於一個Stream進行多次轉換操作,每次都對Stream的每個元素進行轉換,而且是執行多次,這樣時間複雜度就是一個for迴圈裡把所有操作都做掉的N(轉換的次數)倍啊。其實不是這樣的,轉換操作都是lazy的,多個轉換操作只會在匯聚操作(見下節)的時候融合起來,一次迴圈完成。我們可以這樣簡單的理解,Stream裡有個操作函式的集合,每次轉換操作就是把轉換函式放入這個集合中,在匯聚操作的時候迴圈Stream對應的集合,然後對每個元素執行所有的函式。
Stream介面有一些通用的匯聚操作,比如reduce()和collect();也有一些特定用途的匯聚操作,比如sum(),max()和count()。注意:sum方法不是所有的Stream物件都有的,只有IntStream、LongStream和DoubleStream是例項才有。