Java8新特性-說說Stream的應(yīng)用
點擊關(guān)注公眾號,Java干貨及時送達

JDK1.8新特性中較為重要的一塊內(nèi)容就是Stream API了,通過Stream API,我們改變了傳統(tǒng)的集合操作,使得對集合的操作更加簡單和可控。
初體驗
在Stream出現(xiàn)以前,我們是如何對集合進行處理的呢?
比如現(xiàn)在有一個需求,從一個字符串集合中找出長度大于5的字符串,該如何實現(xiàn)呢?代碼如下:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList("apple",?"banana",?"pear",?"orange",?"peach",?"watermelon");
????for?(int?i?=?0;?i?????????if?(list.get(i).length()?>?5)?{
????????????System.out.println(list.get(i));
????????}
????}
}
這段代碼非常基礎(chǔ),你也可以使用增強for循環(huán)實現(xiàn)這一需求:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList("apple",?"banana",?"pear",?"orange",?"peach",?"watermelon");
????for?(String?str?:?list)?{
????????if?(str.length()?>?5)?{
????????????System.out.println(str);
????????}
????}
}
如果要你將字符串長度大于5的字符串收集起來存入一個新的集合,你就得新建一個集合,然后在循環(huán)里進行添加:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList("apple",?"banana",?"pear",?"orange",?"peach",?"watermelon");
????List?newList?=?new?ArrayList<>();
????for?(String?str?:?list)?{
????????if?(str.length()?>?5)?{
????????????newList.add(str);
????????}
????}
????System.out.println(newList);
}
上述的這些操作,在Stream API中將會非常容易實現(xiàn),先看一個簡單的例子:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList("apple",?"banana",?"pear",?"orange",?"peach",?"watermelon");
????list.forEach(System.out::println);
}
遍歷一個集合變得非常簡單,雖然這并不是Steam的API,但這一特性仍然是在JDK1.8之后才支持的,接下來我們就用Steam實現(xiàn)一下將字符串長度大于5的字符串收集成為一個新集合的需求:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList("apple",?"banana",?"pear",?"orange",?"peach",?"watermelon");
????List?newList?=?list.stream().map(str?->?{
????????if?(str.length()?>?5)?{
????????????return?str;
????????}
????????return?null;
????}).collect(Collectors.toList());
????System.out.println(newList);
}
我們首先不必糾結(jié)為什么這么寫,先來看看它的效果:
[null,?banana,?null,?orange,?null,?watermelon]
新集合中雖然都是長度大于5的字符串,但多了一些null值,所以,使用Stream的filter顯然更適合這一需求:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList("apple",?"banana",?"pear",?"orange",?"peach",?"watermelon");
????List?newList?=?list.stream().filter(str?->?{
????????return?str.length()?>?5;
????}).collect(Collectors.toList());
????System.out.println(newList);
}
運行結(jié)果:
[banana,?orange,?watermelon]
熟悉Lambda表達式的同學(xué)肯定能發(fā)現(xiàn),這一寫法還能能進行簡化:
List?newList?=?list.stream().filter(str?->?str.length()?>?5).collect(Collectors.toList());
Stream的創(chuàng)建
見識到了Stream API的廣大神通了吧,接下來我們回到最基本的問題,如何創(chuàng)建一個Stream?
public?static?void?main(String[]?args)?{
????Stream?stream?=?Stream.of(1,?2,?3);
}
最簡單的方式便是使用of方法,通過of方法可以創(chuàng)建一個由指定元素組成的Stream,但通常情況下我們并不會使用到該方法,而是使用下面的幾個方法:
private?static?void?method(List?list,String[]?array) ?{
????Stream?listStream?=?list.stream();
????Stream?arrayStream?=?Arrays.stream(array);
}
Stream還支持從指定范圍的數(shù)組中創(chuàng)建一個流(集合不支持):
private?static?void?method(List?list,String[]?array) ?{
????Stream?stream?=?Arrays.stream(array,?0,?5);
}
創(chuàng)建一個不包含任何元素的流:
public?static?void?main(String[]?args)?{
????Stream使用generate方法創(chuàng)建流:
public?static?void?main(String[]?args)?{
????Stream?stream?=?Stream.generate(Math::random);
}
創(chuàng)建流的方式還有很多,這里就不一一列舉了。
map方法
在最初的例子中,我們用到了map和filter方法,它們的作用分別是什么以及它們的用法是如何的呢?先來所說map方法,map方法類似于遍歷操作,它會得到Stream中每個元素的映射,并對其一一生效,比如:
public?static?void?main(String[]?args)?{
????Stream?stream?=?Stream.of(1,?2,?3,?4,?5);
????List?newList?=?stream.map(num?->?{
????????return?num?+?1;
????}).collect(Collectors.toList());
????System.out.println(newList);
}
這段程序的作用就是對流中的每個元素都做加1操作,運行結(jié)果如下:
[2,?3,?4,?5,?6]
對于那些需要對集合/流中的每個元素都要做相同操作的需求,就非常適合使用map方法,比如前后端分離開發(fā)中,后端經(jīng)常需要從數(shù)據(jù)庫中查詢出數(shù)據(jù),然后將其封裝成VO傳遞給前端,這一需求就能夠使用map方法實現(xiàn):
private?static?void?method()?{
????List?userList?=?userMapper.findAll();
????List filter方法
filter方法與map類似,它也會作用于流中的每個元素,但與其不同的是,filter方法是用來做過濾操作的,在filter中我們需要返回一個boolean類型的值,如果返回為true,則說明該值是我們需要的,如果為false,該值就會被拋棄掉,比如:
public?static?void?main(String[]?args)?{
????Stream?stream?=?Stream.of(1,?35,?7,?4,?3,?5,?54,?57,?36);
????List?newList?=?stream.filter(num?->?{
????????return?num?>?30;
????}).collect(Collectors.toList());
????System.out.println(newList);
}
這段程序的作用是取出流中數(shù)值大于30的數(shù),運行結(jié)果如下:
[35,?54,?57,?36]
filter可以用來對流中的數(shù)據(jù)做過濾處理,比如只想要性別為男的數(shù)據(jù);只想要工資超過2萬的員工信息等等。
其它操作
Stream的神奇操作遠不止這些,下面再介紹一些比較常用的功能,比如找出一個集合中的最大值:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList(11,?232,?45,?6346,?14);
????int?max?=?0;
????for?(Integer?num?:?list)?{
????????if(num?>?max){
????????????max?=?num;
????????}
????}
????System.out.println(max);
}
而如果使用Stream,它將變得非常簡單:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList(11,?232,?45,?6346,?14);
????Optional?max?=?list.stream().max(Comparator.naturalOrder());
????System.out.println(max.get());
}
求最小值,只需使用min方法即可:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList(11,?232,?45,?6346,?14);
????Optional?max?=?list.stream().min(Comparator.naturalOrder());
????System.out.println(max.get());
}
求和:
public?static?void?main(String[]?args)?{
????List?list?=?Arrays.asList(11,?232,?45,?6346,?14);
????Optional?sum?=?list.stream().reduce(Integer::sum);
????System.out.println(sum.get());
}
注意事項
在使用Strema API時需要注意的地方就是Lambda表達式的編寫,如:
List?list?=?wordList.stream().filter(word?->?{
????return?word.startsWith("p");
list.forEach(System.out::println);
對于這樣的一段程序,因為Lambda表達式體中僅包含了一條返回語句,所以表達式可以簡寫:
List?list?=?wordList.stream().
????filter(word?->?word.startsWith("p")).collect(Collectors.toList());
list.forEach(System.out::println);
又比如:
List?numList?=?Arrays.asList(1,?2,?3,?4,?5,?6,?7,?8,?9,?10);
Optional?sum?=?numList.stream().reduce((x,?y)?->?{
????return?x?+?y;
});
System.out.println(sum);
首先因為Lambda表達式體僅包含一條返回語句,所以可以簡寫:
Optional?sum?=?numList.stream().reduce((x,?y)?->?x?+?y);
我們也可以調(diào)用Integer類的靜態(tài)方法sum進行求和:
Optional?sum?=?numList.stream().reduce((x,?y)?->?Integer.sum(x,?y));
又因為Lambda表達式體中僅包含一條語句,且該語句調(diào)用了一個絕對存在的方法,則可以簡寫為:
Optional?sum?=?numList.stream().reduce(Integer::sum);
這就是Lambda表達式中的方法引用,具體細節(jié)可以去了解一下Lambda的相關(guān)內(nèi)容。
最后就是Stream的一些其他類型,當(dāng)你使用of方法創(chuàng)建一個流時:
Stream?stream?=?Stream.of(1,?2,?3,?4,?5,?6,?7,?8,?9,?10);
調(diào)用Stream類的of方法傳入的是基本類型數(shù)據(jù),但是會得到一個包裝類型的Stream,我們知道,基本類型需要裝箱成為包裝類型,這一操作在數(shù)據(jù)量龐大的情況下是比較低效的,Stream API也考慮到這一點,所以提供了對應(yīng)包裝類型的Strema,如下:
IntStream?intStream?=?IntStream.of(1,?2,?'a',?'z');
LongStream?longStream?=?LongStream.of(10L);
DoubleStream?doubleStream?=?DoubleStream.of(1.1f,?2.2);
往 期 推 薦
1、Log4j2維護者吐槽沒工資還要挨罵,GO安全負責(zé)人建議開源作者向公司收費
4、Windows重要功能被閹割,全球用戶怒噴數(shù)月后微軟終于悔改
點分享
點收藏
點點贊
點在看





