前言
前段時間公司書架多了一本《java8 實戰(zhàn)》,畢竟久聞lambda的大名,于是借來一閱。這一看,簡直是驚為天人啊,lambda,stream,java8里簡直是滿腦子騷操作,看我的一愣一愣的。我甚至是第一次感覺到了什么叫優(yōu)雅。
本文主要介紹java8中的流處理,看看java8是怎么愉快的玩耍集合的,讓我們來一起感受java8的魅力吧!
我就隨便舉個例子,看看stream有多優(yōu)雅。
1
2
3
4
5
6
7
|
// 對蘋果按顏色匯總并績數(shù)量 map<string, long > applecount = apples.stream() .collect(groupingby(apple::getcolor, counting())); // 過濾掉顏色為黑色的蘋果,并匯總好蘋果的總金額 double sum = apples.stream() .filter(i-> "black" .equals(i.getcolor())) .collect(tolist); |
一、lambda表達式
雖然本文重點是stream,但是stream中需要傳遞lambda表達式,所以簡單介紹一下lambda表達式。lambda表達式其實就是匿名函數(shù)(anonymous function),是指一類無需定義標識符的函數(shù)或子程序。
java中匿名函數(shù)的表現(xiàn)形式,只留下入?yún)⒑头椒w中的內(nèi)容
1
2
3
4
5
6
|
// 普通函數(shù) public void run(string s){ system.out.print(s+ "哈哈" ); } // 我不要名字啦!??! (s)->system.out.print(s+ "哈哈" ) |
誒,過去我們都用對象調(diào)方法的,你弄這個沒名的東西啥時候用啊?
java中我們通過函數(shù)式接口來使用這種匿名函數(shù)。
函數(shù)式接口
1.java中只包含一個未實現(xiàn)方法的接口。其中可以有與object中同名的方法和默認方法(java8中接口方法可以有默認實現(xiàn))。
2.java中函數(shù)式接口使用@functionalinterface進行注解。runnable、comparator都是函數(shù)式接口。
3.java.util.function包下為我們提供很多常用的函數(shù)式接口,例如function等。
用法舉例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
// 實現(xiàn)runnable中的run方法,替代匿名內(nèi)部類。 runnable r = ()->system.out.print( "哈哈" ); // 作為參數(shù)傳遞。 new thread(()-> system.out.println( "haha" )).start(); arraylist<apple> list = new arraylist<>(); list.foreach(i-> system.out.println(i.getweight())); // 簡化策略模式 public static list<apple> filterapples(list<apple> inventory,applepredicate p){ list<apple> apples = new arraylist<>(); for (apple apple : inventory){ if (p.test(apple)){ apples.add(apple); } } return apples; } public class bigapple implement applepredicate{ @override public boolean test(apple a){ if (a.getweight> 10 ){ return a } } } // 這是個簡單的策略模式,根據(jù)用戶的需要,創(chuàng)建不同的接口applepredicate實現(xiàn)類,調(diào)用時傳入不同的實現(xiàn)類就可以,但問題是如果需求過多,創(chuàng)建的實現(xiàn)類也會很多,過于臃腫不方便管理。 xx.filterapple(inventory, new bigapple); // 使用lambda表達式,不在需要創(chuàng)建bigapple類 xx.filterapple(inventory,i->(i.getweight> 10 )); |
使用lambda表達式可以簡化大量的模板代碼,并且可以向方法直接傳遞代碼。
總之
方法出參入?yún)碜院瘮?shù)式接口
1
2
3
4
5
6
7
8
9
10
|
//入?yún),返回void (s)->system.out.println(s); //入?yún)⒖眨祷豽oid ()->system.out.print( "haha" ); //入?yún),返回i+1 i->i+ 1 //后面寫代碼塊 apple->{ if (apple.getweiht> 5 ) return "big" ; else return "small" ; } |
好了,不多啰嗦了,如果感興趣推薦下面的文章或《java8實戰(zhàn)》的前三章。
二、stream
流是什么?
java api的新成員,它允許你使用聲明式方式處理數(shù)據(jù)集合(類似sql,通過查詢語句表達,而不是臨時編寫一個實現(xiàn))。
如果有人說lambda表達式不易于理解,那還勉強可以接受(其實過于復(fù)雜的lambda缺失不好閱讀,但通常lambda不會做太復(fù)雜的實現(xiàn)),但流真的非常的易懂易用。這個語法糖真的是甜死了。
注意事項:
1.流只能使用一次,遍歷結(jié)束就代表這個流被消耗掉了
2.流對集合的操作屬于內(nèi)部迭代,是流幫助我們操作,而不是外部迭代
3.流操作包含:數(shù)據(jù)源,中間操作鏈,終端操作三個部分。
基礎(chǔ)流操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
list< double > collect = list.stream() // 過濾掉黑色的蘋果 .filter(i -> "black" .equals(i.getcolor())) // 讓蘋果按照重量個價格排序 .sorted(comparator.comparing(apple::getweight) .thencomparing(i->i.getprice())) // 篩選掉重復(fù)的數(shù)據(jù) .distinct() // 只要蘋果的價格 .map(apple::getprice) // 只留下前兩條數(shù)據(jù) .limit( 2 ) // 以集合的形式返回 .collect(tolist()); // 循環(huán)打印列表中元素 list.foreach(i->system.out.print(i)); |
apple::getprince<=>i -> i.getprince()
可以看做是僅涉及單一方法的語法糖,效果與lambda表達式相同,但可讀性更好。
同理
下面列表為常見操作
中間
操作 | 類型 | 作用 | 函數(shù)描述 | 函數(shù) |
---|---|---|---|---|
filter | 中間 | 過濾 | t -> boolean |
predicate |
sorted | 中間 | 排序 | (t,t)->int |
comparator |
map | 中間 | 映射 | t->r | function<t,r> |
limit | 中間 | 截斷 | ||
distinct | 中間 | 去重,根據(jù)equals方法 | ||
skip | 中間 | 跳過前n個元素 |
終端
操作 | 類型 | 作用 |
---|---|---|
foreach | 終端 | 消費流中的每個元素,使用lambda進行操作 |
count | 終端 | 返回元素個數(shù),long |
collect | 終端 | 將流歸約成一個集合,如list,map甚至是integer |
篩選與切片
1
2
3
4
5
6
7
8
9
10
|
list<string> strings = arrays.aslist( "hello" , "world" ); list<string> collect1 = strings.stream() // string映射成string[] .map(i -> i.split( "" )) // arrays::stream 數(shù)據(jù)數(shù)組,返回一個流string[]->stream<string> // flatmap各數(shù)組并不分別映射成一個流,而是映射成流的內(nèi)容 stream<string>->stream .flatmap(arrays::stream) .collect(tolist()); system.out.println(collect); ----->輸出 [h, e, l, l, o, w, o, r, l, d] |
歸約操作reduce
1
2
3
4
5
6
7
8
9
10
11
12
|
list<integer> integers = arrays.aslist( 12 , 3 , 45 , 3 , 2 ,- 1 ); // 有初始值的疊加操作 integer reduce = integers.stream().reduce( 3 , (i, j) -> i + j); integer reduce2 = integers.stream().reduce( 5 , (x, y) -> x < y ? x : y); // 無初始值的疊加操作 optional<integer> reduce1 = integers.stream().reduce((i, j) -> i + j); // 無初始值的最大值 optional<integer> reduce4 = integers.stream().reduce(integer::min); // 無初始值的最大值 optional<integer> reduce5 = integers.stream().reduce(integer::max); // 求和 optional<integer> reduce6 = integers.stream().reduce(integer::sum); |
reduce做的事情是取兩個數(shù)進行操作,結(jié)果返回取下一個數(shù)操作,以次類推。
optional是java8引入的新類,避免造成空指針異常,在集合為空時,結(jié)果會包在optional中,可以用ispresent()方法來判斷是否為空值。
無初始值的情況下可能為空,故返回optional
中間
操作 | 類型 | 作用 | 函數(shù)描述 | 函數(shù) |
---|---|---|---|---|
flatmap | 中間 | 使通過的流返回內(nèi)容 | t -> boolean |
predicate |
終端
操作 | 類型 | 作用 |
---|---|---|
anymatch | 終端 | 返回boolean,判斷是否有符合條件內(nèi)容 |
nonematch | 終端 | 返回boolean,判斷是否無符合條件內(nèi)容 |
allmatch | 終端 | 返回boolean,判斷是全為符合條件內(nèi)容 |
findany | 終端 |
optional |
findfirst | 終端 |
optional |
reduce | 終端 |
optional |
數(shù)值流
包裝類型的各種操作都會有拆箱操作和裝箱操作,嚴重影響性能。所以java8為我們提供了原始數(shù)值流。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 數(shù)值流求平均值 optionaldouble average = apples.stream() .maptodouble(apple::getprice) .average(); // 數(shù)值流求和 optionaldouble average = apples.stream() .maptodouble(apple::getprice) .sum(); // 數(shù)值流求最大值,沒有則返回2 double v = apples.stream() .maptodouble(apple::getprice) .max().orelse( 2 ); // 生成隨機數(shù) intstream s = intstream.rangeclosed( 1 , 100 ); |
下面列表為常見數(shù)值流操作操作
中間
操作 | 類型 | 作用 |
---|---|---|
rangeclosed(1,100) | 中間 | 生成隨機數(shù)(1,100] |
range(1,100) | 中間 | 生成隨機數(shù)(1,100) |
boxed() | 中間 | 包裝成一般流 |
maptoobj | 中間 | 返回為對象流 |
maptoint | 中間 | 映射為數(shù)值流 |
終端,終端操作與list一般流類似
構(gòu)建流
值創(chuàng)建
1
|
stream<string> s = stream.of( "java" , "python" ); |
數(shù)組創(chuàng)建
1
2
|
int [] i = { 2 , 3 , 4 , 5 }; stream< int > = arrays.stream(i); |
由文件生成,nio api已經(jīng)更新,以便利用stream api
1
|
stream<string> s = files.lines(paths.get( "data.txt" ),charset.defaultcharset()); |
由函數(shù)創(chuàng)建流:無限流
1
2
3
4
5
6
7
8
|
// 迭代 stream.iterate( 0 ,n->n+ 2 ) .limit( 10 ) .foreach(system.out::println); // 生成,需要傳遞實現(xiàn)supplier<t>類型的lambda提供的新值 stream.generate(math.random) .limit( 5 ) .foreach(system.out::println); |
三、總結(jié)
至此,本文講述了常見的流操作,目前排序、篩選、求和、歸約等大多數(shù)操作我們都能實現(xiàn)了。與過去相比,操作集合變的簡單多了,代碼也變的更加簡練明了。
目前vert.x,spring新出的webflux都通過lambda表達式來簡化代碼,不久的將來,非阻塞式框架的大行其道時,lambda表達式必將變的更加重要!
至于開篇見到的分組!??!下篇文章見~
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://www.cnblogs.com/cdream-zs/p/10504499.html