都9102年了,你還用for循環(huán)操作集合嗎
前言
前段時(shí)間公司書架多了一本《Java8 實(shí)戰(zhàn)》,畢竟久聞lambda的大名,于是借來(lái)一閱。這一看,簡(jiǎn)直是驚為天人啊,lambda,stream,java8里簡(jiǎn)直是滿腦子騷操作,看我的一愣一愣的。我甚至是第一次感覺(jué)到了什么叫優(yōu)雅。

本文主要介紹java8中的流處理,看看java8是怎么愉快的玩耍集合的,讓我們來(lái)一起感受java8的魅力吧!
我就隨便舉個(gè)例子,看看Stream有多優(yōu)雅。
// 對(duì)蘋果按顏色匯總并績(jī)數(shù)量 Map<String, Long> appleCount = apples.stream() .collect(groupingBy(Apple::getColor, counting())); // 過(guò)濾掉顏色為黑色的蘋果,并匯總好蘋果的總金額 Double sum = apples.stream() .filter(i->"black".equals(i.getColor())) .collect(toList);
一、lambda表達(dá)式
雖然本文重點(diǎn)是stream,但是stream中需要傳遞lambda表達(dá)式,所以簡(jiǎn)單介紹一下lambda表達(dá)式。lambda表達(dá)式其實(shí)就是匿名函數(shù)(anonymous function),是指一類無(wú)需定義標(biāo)識(shí)符的函數(shù)或子程序。
java中匿名函數(shù)的表現(xiàn)形式,只留下入?yún)⒑头椒w中的內(nèi)容
// 普通函數(shù)
public void run(String s){
System.out.print(s+"哈哈");
}
// 我不要名字啦?。?!
(s)->System.out.print(s+"哈哈")
誒,過(guò)去我們都用對(duì)象調(diào)方法的,你弄這個(gè)沒(méi)名的東西啥時(shí)候用啊?
java中我們通過(guò)函數(shù)式接口來(lái)使用這種匿名函數(shù)。
函數(shù)式接口
1.java中只包含一個(gè)未實(shí)現(xiàn)方法的接口。其中可以有與Object中同名的方法和默認(rèn)方法(java8中接口方法可以有默認(rèn)實(shí)現(xiàn))。
2.java中函數(shù)式接口使用@FunctionalInterface進(jìn)行注解。Runnable、Comparator都是函數(shù)式接口。
3.java.util.function包下為我們提供很多常用的函數(shù)式接口,例如Function等。
用法舉例:
// 實(shí)現(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()));
// 簡(jiǎn)化策略模式
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
}
}
}
// 這是個(gè)簡(jiǎn)單的策略模式,根據(jù)用戶的需要,創(chuàng)建不同的接口ApplePredicate實(shí)現(xiàn)類,調(diào)用時(shí)傳入不同的實(shí)現(xiàn)類就可以,但問(wèn)題是如果需求過(guò)多,創(chuàng)建的實(shí)現(xiàn)類也會(huì)很多,過(guò)于臃腫不方便管理。
xx.filterApple(inventory,new BigApple);
// 使用lambda表達(dá)式,不在需要?jiǎng)?chuàng)建BigApple類
xx.filterApple(inventory,i->(i.getWeight>10));
使用lambda表達(dá)式可以簡(jiǎn)化大量的模板代碼,并且可以向方法直接傳遞代碼。
總之
方法出參入?yún)?lái)自函數(shù)式接口
//入?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實(shí)戰(zhàn)》的前三章。
二、Stream
流是什么?
Java API的新成員,它允許你使用聲明式方式處理數(shù)據(jù)集合(類似sql,通過(guò)查詢語(yǔ)句表達(dá),而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。
如果有人說(shuō)lambda表達(dá)式不易于理解,那還勉強(qiáng)可以接受(其實(shí)過(guò)于復(fù)雜的lambda缺失不好閱讀,但通常lambda不會(huì)做太復(fù)雜的實(shí)現(xiàn)),但流真的非常的易懂易用。這個(gè)語(yǔ)法糖真的是甜死了。
注意事項(xiàng):
1.流只能使用一次,遍歷結(jié)束就代表這個(gè)流被消耗掉了
2.流對(duì)集合的操作屬于內(nèi)部迭代,是流幫助我們操作,而不是外部迭代
3.流操作包含:數(shù)據(jù)源,中間操作鏈,終端操作三個(gè)部分。
基礎(chǔ)流操作
List<Double> collect = list.stream() // 過(guò)濾掉黑色的蘋果 .filter(i -> "black".equals(i.getColor())) // 讓蘋果按照重量個(gè)價(jià)格排序 .sorted(Comparator.comparing(Apple::getWeight) .thenComparing(i->i.getPrice())) // 篩選掉重復(fù)的數(shù)據(jù) .distinct() // 只要蘋果的價(jià)格 .map(Apple::getPrice) // 只留下前兩條數(shù)據(jù) .limit(2) // 以集合的形式返回 .collect(toList()); // 循環(huán)打印列表中元素 list.forEach(i->System.out.print(i));
Apple::getPrince<=>i -> i.getPrince()可以看做是僅涉及單一方法的語(yǔ)法糖,效果與lambda表達(dá)式相同,但可讀性更好。
同理
下面列表為常見(jiàn)操作
中間
| 操作 | 類型 | 作用 | 函數(shù)描述 | 函數(shù) |
|---|---|---|---|---|
| filter | 中間 | 過(guò)濾 | T -> boolean | Predicate |
| sorted | 中間 | 排序 | (T,T)->int | Comparator |
| map | 中間 | 映射 | T->R | Function<T,R> |
| limit | 中間 | 截?cái)?/td> | ||
| distinct | 中間 | 去重,根據(jù)equals方法 | ||
| skip | 中間 | 跳過(guò)前n個(gè)元素 |
終端
| 操作 | 類型 | 作用 |
|---|---|---|
| forEach | 終端 | 消費(fèi)流中的每個(gè)元素,使用lambda進(jìn)行操作 |
| count | 終端 | 返回元素個(gè)數(shù),long |
| collect | 終端 | 將流歸約成一個(gè)集合,如List,Map甚至是Integer |
篩選與切片
List<String> strings = Arrays.asList("Hello", "World");
List<String> collect1 = strings.stream()
// String映射成String[]
.map(i -> i.split(""))
// Arrays::Stream 數(shù)據(jù)數(shù)組,返回一個(gè)流String[]->Stream<String>
// flatMap各數(shù)組并不分別映射成一個(gè)流,而是映射成流的內(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
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); // 無(wú)初始值的疊加操作 Optional<Integer> reduce1 = integers.stream().reduce((i, j) -> i + j); // 無(wú)初始值的最大值 Optional<Integer> reduce4 = integers.stream().reduce(Integer::min); // 無(wú)初始值的最大值 Optional<Integer> reduce5 = integers.stream().reduce(Integer::max); // 求和 Optional<Integer> reduce6 = integers.stream().reduce(Integer::sum);
reduce做的事情是取兩個(gè)數(shù)進(jìn)行操作,結(jié)果返回取下一個(gè)數(shù)操作,以次類推。
Optional是java8引入的新類,避免造成空指針異常,在集合為空時(shí),結(jié)果會(huì)包在Optional中,可以用isPresent()方法來(lái)判斷是否為空值。
無(wú)初始值的情況下可能為空,故返回Optional
中間
| 操作 | 類型 | 作用 | 函數(shù)描述 | 函數(shù) |
|---|---|---|---|---|
| flatmap | 中間 | 使通過(guò)的流返回內(nèi)容 | T -> boolean | Predicate |
終端
| 操作 | 類型 | 作用 |
|---|---|---|
| anyMatch | 終端 | 返回boolean,判斷是否有符合條件內(nèi)容 |
| noneMatch | 終端 | 返回boolean,判斷是否無(wú)符合條件內(nèi)容 |
| allMatch | 終端 | 返回boolean,判斷是全為符合條件內(nèi)容 |
| findAny | 終端 | Optional |
| findFirst | 終端 | Optional |
| reduce | 終端 | Optional |
數(shù)值流
包裝類型的各種操作都會(huì)有拆箱操作和裝箱操作,嚴(yán)重影響性能。所以Java8為我們提供了原始數(shù)值流。
// 數(shù)值流求平均值 OptionalDouble average = apples.stream() .mapToDouble(Apple::getPrice) .average(); // 數(shù)值流求和 OptionalDouble average = apples.stream() .mapToDouble(Apple::getPrice) .sum(); // 數(shù)值流求最大值,沒(méi)有則返回2 double v = apples.stream() .mapToDouble(Apple::getPrice) .max().orElse(2); // 生成隨機(jī)數(shù) IntStream s = IntStream.rangeClosed(1,100);
下面列表為常見(jiàn)數(shù)值流操作操作
中間
| 操作 | 類型 | 作用 |
|---|---|---|
| rangeClosed(1,100) | 中間 | 生成隨機(jī)數(shù)(1,100] |
| range(1,100) | 中間 | 生成隨機(jī)數(shù)(1,100) |
| boxed() | 中間 | 包裝成一般流 |
| mapToObj | 中間 | 返回為對(duì)象流 |
| mapToInt | 中間 | 映射為數(shù)值流 |
終端,終端操作與List一般流類似
構(gòu)建流
值創(chuàng)建
Stream<String> s = Stream.of("java","python");
數(shù)組創(chuàng)建
int[] i = {2,3,4,5};
Stream<int> = Arrays.stream(i);
由文件生成,NIO API已經(jīng)更新,以便利用Stream API
Stream<String> s = Files.lines(Paths.get("data.txt"),Charset.defaultCharset());
由函數(shù)創(chuàng)建流:無(wú)限流
// 迭代 Stream.iterate(0,n->n+2) .limit(10) .forEach(System.out::println); // 生成,需要傳遞實(shí)現(xiàn)Supplier<T>類型的Lambda提供的新值 Stream.generate(Math.random) .limit(5) .forEach(System.out::println);
三、總結(jié)
至此,本文講述了常見(jiàn)的流操作,目前排序、篩選、求和、歸約等大多數(shù)操作我們都能實(shí)現(xiàn)了。與過(guò)去相比,操作集合變的簡(jiǎn)單多了,代碼也變的更加簡(jiǎn)練明了。
目前Vert.x,Spring新出的WebFlux都通過(guò)lambda表達(dá)式來(lái)簡(jiǎn)化代碼,不久的將來(lái),非阻塞式框架的大行其道時(shí),lambda表達(dá)式必將變的更加重要!
至于開(kāi)篇見(jiàn)到的分組?。?!下篇文章見(jiàn)~
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
GateWay動(dòng)態(tài)路由與負(fù)載均衡詳細(xì)介紹
這篇文章主要介紹了GateWay動(dòng)態(tài)路由與負(fù)載均衡,GateWay支持自動(dòng)從注冊(cè)中心中獲取服務(wù)列表并訪問(wèn),即所謂的動(dòng)態(tài)路由2022-11-11
Java8 Collectors求和功能的自定義擴(kuò)展操作
這篇文章主要介紹了Java8 Collectors求和功能的自定義擴(kuò)展操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Java無(wú)限級(jí)樹(遞歸)超實(shí)用案例
下面小編就為大家?guī)?lái)一篇Java無(wú)限級(jí)樹(遞歸)超實(shí)用案例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
java CompletableFuture實(shí)現(xiàn)異步編排詳解
這篇文章主要為大家介紹了java CompletableFuture實(shí)現(xiàn)異步編排詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
使用json字符串插入節(jié)點(diǎn)或者覆蓋節(jié)點(diǎn)
這篇文章主要介紹了使用json字符串插入節(jié)點(diǎn)或者覆蓋節(jié)點(diǎn)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
spring?boot?實(shí)現(xiàn)一個(gè)?禁止重復(fù)請(qǐng)求的方法
這篇文章主要介紹了spring?boot?實(shí)現(xiàn)一個(gè)?禁止重復(fù)請(qǐng)求,當(dāng)重復(fù)請(qǐng)求該方法時(shí),會(huì)返回"Duplicate?request",避免重復(fù)執(zhí)行相同的操作,需要的朋友可以參考下2024-03-03

