玩轉(zhuǎn) Java8 Stream,常用方法大合集
來(lái)源:blog.csdn.net/y_k_y/article/details/84633001
一、概述
Stream 是 Java8 中處理集合的關(guān)鍵抽象概念,它可以指定你希望對(duì)集合進(jìn)行的操作,可以執(zhí)行非常復(fù)雜的查找、過(guò)濾和映射數(shù)據(jù)等操作。使用Stream API 對(duì)集合數(shù)據(jù)進(jìn)行操作,就類似于使用 SQL 執(zhí)行的數(shù)據(jù)庫(kù)查詢。也可以使用 Stream API 來(lái)并行執(zhí)行操作。
簡(jiǎn)而言之,Stream API 提供了一種高效且易于使用的處理數(shù)據(jù)的方式。
特點(diǎn):
不是數(shù)據(jù)結(jié)構(gòu),不會(huì)保存數(shù)據(jù)。
不會(huì)修改原來(lái)的數(shù)據(jù)源,它會(huì)將操作后的數(shù)據(jù)保存到另外一個(gè)對(duì)象中。(保留意見(jiàn):畢竟peek方法可以修改流中元素)
惰性求值,流在中間處理過(guò)程中,只是對(duì)操作進(jìn)行了記錄,并不會(huì)立即執(zhí)行,需要等到執(zhí)行終止操作的時(shí)候才會(huì)進(jìn)行實(shí)際的計(jì)算。
二、分類

無(wú)狀態(tài): 指元素的處理不受之前元素的影響;
有狀態(tài): 指該操作只有拿到所有元素之后才能繼續(xù)下去。
非短路操作: 指必須處理所有元素才能得到最終結(jié)果;
短路操作: 指遇到某些符合條件的元素就可以得到最終結(jié)果,如 A || B,只要A為true,則無(wú)需判斷B的結(jié)果。
三、具體用法
1. 流的常用創(chuàng)建方法
1.1 使用Collection下的 stream() 和 parallelStream() 方法
List?list?=?new?ArrayList<>();
Stream?stream?=?list.stream();?//獲取一個(gè)順序流
Stream?parallelStream?=?list.parallelStream();?//獲取一個(gè)并行流
1.2 使用Arrays 中的stream()方法,將數(shù)組轉(zhuǎn)成流
Integer[]?nums?=?new?Integer[10];
Stream?stream?=?Arrays.stream(nums);
1.3 使用Stream中的靜態(tài)方法:of()、iterate()、generate()
Stream?stream?=?Stream.of(1,2,3,4,5,6);
?
Stream?stream2?=?Stream.iterate(0,?(x)?->?x?+?2).limit(6);
stream2.forEach(System.out::println);?//?0?2?4?6?8?10
?
Stream?stream3?=?Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
1.4 使用 BufferedReader.lines() 方法,將每行內(nèi)容轉(zhuǎn)成流
BufferedReader?reader?=?new?BufferedReader(new?FileReader("F:\\test_stream.txt"));
Stream?lineStream?=?reader.lines();
lineStream.forEach(System.out::println);
1.5 使用 Pattern.splitAsStream() 方法,將字符串分隔成流
Pattern?pattern?=?Pattern.compile(",");
Stream?stringStream?=?pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
2. 流的中間操作
2.1 篩選與切片
filter:過(guò)濾流中的某些元素limit(n):獲取n個(gè)元素skip(n):跳過(guò)n元素,配合limit(n)可實(shí)現(xiàn)分頁(yè)distinct:通過(guò)流中元素的hashCode()和equals()去除重復(fù)元素
Stream?stream?=?Stream.of(6,?4,?6,?7,?3,?9,?8,?10,?12,?14,?14);
?
Stream?newStream?=?stream.filter(s?->?s?>?5)?//6?6?7?9?8?10?12?14?14
????????.distinct()?//6?7?9?8?10?12?14
????????.skip(2)?//9?8?10?12?14
????????.limit(2);?//9?8
newStream.forEach(System.out::println);
2.2 映射
map: 接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上,并將其映射成一個(gè)新的元素。 flatMap: 接收一個(gè)函數(shù)作為參數(shù),將流中的每個(gè)值都換成另一個(gè)流,然后把所有流連接成一個(gè)流。
List?list?=?Arrays.asList("a,b,c",?"1,2,3");
?
//將每個(gè)元素轉(zhuǎn)成一個(gè)新的且不帶逗號(hào)的元素
Stream?s1?=?list.stream().map(s?->?s.replaceAll(",",?""));
s1.forEach(System.out::println);?//?abc??123
?
Stream?s3?=?list.stream().flatMap(s?->?{
????//將每個(gè)元素轉(zhuǎn)換成一個(gè)stream
????String[]?split?=?s.split(",");
????Stream?s2?=?Arrays.stream(split);
????return?s2;
});
s3.forEach(System.out::println);?//?a?b?c?1?2?3
2.3 排序
sorted():自然排序,流中元素需實(shí)現(xiàn)Comparable接口sorted(Comparator com):定制排序,自定義Comparator排序器
List?list?=?Arrays.asList("aa",?"ff",?"dd");
//String?類自身已實(shí)現(xiàn)Compareable接口
list.stream().sorted().forEach(System.out::println);//?aa?dd?ff
?
Student?s1?=?new?Student("aa",?10);
Student?s2?=?new?Student("bb",?20);
Student?s3?=?new?Student("aa",?30);
Student?s4?=?new?Student("dd",?40);
List?studentList?=?Arrays.asList(s1,?s2,?s3,?s4);
?
//自定義排序:先按姓名升序,姓名相同則按年齡升序
studentList.stream().sorted(
????????(o1,?o2)?->?{
????????????if?(o1.getName().equals(o2.getName()))?{
????????????????return?o1.getAge()?-?o2.getAge();
????????????}?else?{
????????????????return?o1.getName().compareTo(o2.getName());
????????????}
????????}
).forEach(System.out::println);
2.4 消費(fèi)
peek:如同于map,能得到流中的每一個(gè)元素。但map接收的是一個(gè)Function表達(dá)式,有返回值;而peek接收的是Consumer表達(dá)式,沒(méi)有返回值。
Student?s1?=?new?Student("aa",?10);
Student?s2?=?new?Student("bb",?20);
List?studentList?=?Arrays.asList(s1,?s2);
?
studentList.stream()
????????.peek(o?->?o.setAge(100))
????????.forEach(System.out::println);???
?
//結(jié)果:
Student{name='aa',?age=100}
Student{name='bb',?age=100}????????????
3. 流的終止操作
3.1 匹配、聚合操作
allMatch:接收一個(gè) Predicate 函數(shù),當(dāng)流中每個(gè)元素都符合該斷言時(shí)才返回true,否則返回falsenoneMatch:接收一個(gè) Predicate 函數(shù),當(dāng)流中每個(gè)元素都不符合該斷言時(shí)才返回true,否則返回falseanyMatch:接收一個(gè) Predicate 函數(shù),只要流中有一個(gè)元素滿足該斷言則返回true,否則返回falsefindFirst:返回流中第一個(gè)元素findAny:返回流中的任意元素count:返回流中元素的總個(gè)數(shù)max:返回流中元素最大值min:返回流中元素最小值??Java項(xiàng)目分享
List?list?=?Arrays.asList(1,?2,?3,?4,?5);
?
boolean?allMatch?=?list.stream().allMatch(e?->?e?>?10);?//false
boolean?noneMatch?=?list.stream().noneMatch(e?->?e?>?10);?//true
boolean?anyMatch?=?list.stream().anyMatch(e?->?e?>?4);??//true
?
Integer?findFirst?=?list.stream().findFirst().get();?//1
Integer?findAny?=?list.stream().findAny().get();?//1
?
long?count?=?list.stream().count();?//5
Integer?max?=?list.stream().max(Integer::compareTo).get();?//5
Integer?min?=?list.stream().min(Integer::compareTo).get();?//1
3.2 規(guī)約操作
Optional:第一次執(zhí)行時(shí),accumulator函數(shù)的第一個(gè)參數(shù)為流中的第一個(gè)元素,第二個(gè)參數(shù)為流中元素的第二個(gè)元素;第二次執(zhí)行時(shí),第一個(gè)參數(shù)為第一次函數(shù)執(zhí)行的結(jié)果,第二個(gè)參數(shù)為流中的第三個(gè)元素;依次類推。reduce(BinaryOperator accumulator) T reduce(T identity, BinaryOperator:流程跟上面一樣,只是第一次執(zhí)行時(shí),accumulator) accumulator函數(shù)的第一個(gè)參數(shù)為identity,而第二個(gè)參數(shù)為流中的第一個(gè)元素。U reduce(U identity,BiFunction accumulator,BinaryOperator combiner):在串行流(stream)中,該方法跟第二個(gè)方法一樣,即第三個(gè)參數(shù)combiner不會(huì)起作用。在并行流(parallelStream)中,我們知道流被fork join出多個(gè)線程進(jìn)行執(zhí)行,此時(shí)每個(gè)線程的執(zhí)行流程就跟第二個(gè)方法reduce(identity,accumulator)一樣,而第三個(gè)參數(shù)combiner函數(shù),則是將每個(gè)線程的執(zhí)行結(jié)果當(dāng)成一個(gè)新的流,然后使用第一個(gè)方法reduce(accumulator)流程進(jìn)行規(guī)約。?Java項(xiàng)目分享
//經(jīng)過(guò)測(cè)試,當(dāng)元素個(gè)數(shù)小于24時(shí),并行時(shí)線程數(shù)等于元素個(gè)數(shù),當(dāng)大于等于24時(shí),并行時(shí)線程數(shù)為16
List?list?=?Arrays.asList(1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,?16,?17,?18,?19,?20,?21,?22,?23,?24);
?
Integer?v?=?list.stream().reduce((x1,?x2)?->?x1?+?x2).get();
System.out.println(v);???//?300
?
Integer?v1?=?list.stream().reduce(10,?(x1,?x2)?->?x1?+?x2);
System.out.println(v1);??//310
?
Integer?v2?=?list.stream().reduce(0,
????????(x1,?x2)?->?{
????????????System.out.println("stream?accumulator:?x1:"?+?x1?+?"??x2:"?+?x2);
????????????return?x1?-?x2;
????????},
????????(x1,?x2)?->?{
????????????System.out.println("stream?combiner:?x1:"?+?x1?+?"??x2:"?+?x2);
????????????return?x1?*?x2;
????????});
System.out.println(v2);?//?-300
?
Integer?v3?=?list.parallelStream().reduce(0,
????????(x1,?x2)?->?{
????????????System.out.println("parallelStream?accumulator:?x1:"?+?x1?+?"??x2:"?+?x2);
????????????return?x1?-?x2;
????????},
????????(x1,?x2)?->?{
????????????System.out.println("parallelStream?combiner:?x1:"?+?x1?+?"??x2:"?+?x2);
????????????return?x1?*?x2;
????????});
System.out.println(v3);?//197474048
3.3 收集操作
collect:接收一個(gè)Collector實(shí)例,將流中元素收集成另外一個(gè)數(shù)據(jù)結(jié)構(gòu)。
Collector 是一個(gè)接口,有以下5個(gè)抽象方法:
Supplier supplier():創(chuàng)建一個(gè)結(jié)果容器ABiConsumer accumulator():消費(fèi)型接口,第一個(gè)參數(shù)為容器A,第二個(gè)參數(shù)為流中元素T。BinaryOperator combiner():函數(shù)接口,該參數(shù)的作用跟上一個(gè)方法(reduce)中的combiner參數(shù)一樣,將并行流中各個(gè)子進(jìn)程的運(yùn)行結(jié)果(accumulator函數(shù)操作后的容器A)進(jìn)行合并。Function finisher():函數(shù)式接口,參數(shù)為:容器A,返回類型為:collect方法最終想要的結(jié)果R。Set:返回一個(gè)不可變的Set集合,用來(lái)表明該Collector的特征。有以下三個(gè)特征:characteristics() CONCURRENT:表示此收集器支持并發(fā)。(官方文檔還有其他描述,暫時(shí)沒(méi)去探索,故不作過(guò)多翻譯)UNORDERED:表示該收集操作不會(huì)保留流中元素原有的順序。IDENTITY_FINISH:表示finisher參數(shù)只是標(biāo)識(shí)而已,可忽略。?Java項(xiàng)目分享
3.3.1 Collector 工具庫(kù):Collectors
Student?s1?=?new?Student("aa",?10,1);
Student?s2?=?new?Student("bb",?20,2);
Student?s3?=?new?Student("cc",?10,3);
List?list?=?Arrays.asList(s1,?s2,?s3);
?
//裝成list
List?ageList?=?list.stream().map(Student::getAge).collect(Collectors.toList());?//?[10,?20,?10]
?
//轉(zhuǎn)成set
Set?ageSet?=?list.stream().map(Student::getAge).collect(Collectors.toSet());?//?[20,?10]
?
//轉(zhuǎn)成map,注:key不能相同,否則報(bào)錯(cuò)
Map?studentMap?=?list.stream().collect(Collectors.toMap(Student::getName,?Student::getAge));?//?{cc=10,?bb=20,?aa=10}
?
//字符串分隔符連接
String?joinName?=?list.stream().map(Student::getName).collect(Collectors.joining(",",?"(",?")"));?//?(aa,bb,cc)
?
//聚合操作
//1.學(xué)生總數(shù)
Long?count?=?list.stream().collect(Collectors.counting());?//?3
//2.最大年齡?(最小的minBy同理)
Integer?maxAge?=?list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();?//?20
//3.所有人的年齡
Integer?sumAge?=?list.stream().collect(Collectors.summingInt(Student::getAge));?//?40
//4.平均年齡
Double?averageAge?=?list.stream().collect(Collectors.averagingDouble(Student::getAge));?//?13.333333333333334
//?帶上以上所有方法
DoubleSummaryStatistics?statistics?=?list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:"?+?statistics.getCount()?+?",max:"?+?statistics.getMax()?+?",sum:"?+?statistics.getSum()?+?",average:"?+?statistics.getAverage());
?
//分組
Map>?ageMap?=?list.stream().collect(Collectors.groupingBy(Student::getAge));
//多重分組,先根據(jù)類型分再根據(jù)年齡分
Map>>?typeAgeMap?=?list.stream().collect(Collectors.groupingBy(Student::getType,?Collectors.groupingBy(Student::getAge)));
?
//分區(qū)
//分成兩部分,一部分大于10歲,一部分小于等于10歲
Map>?partMap?=?list.stream().collect(Collectors.partitioningBy(v?->?v.getAge()?>?10));
?
//規(guī)約
Integer?allAge?=?list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get();?//40
3.3.2 Collectors.toList() 解析
//toList?源碼
public?static??Collector>?toList()?{
????return?new?CollectorImpl<>((Supplier>)?ArrayList::new,?List::add,
????????????(left,?right)?->?{
????????????????left.addAll(right);
????????????????return?left;
????????????},?CH_ID);
}
?
//為了更好地理解,我們轉(zhuǎn)化一下源碼中的lambda表達(dá)式
public??Collector>?toList()?{
????Supplier>?supplier?=?()?->?new?ArrayList();
????BiConsumer,?T>?accumulator?=?(list,?t)?->?list.add(t);
????BinaryOperator>?combiner?=?(list1,?list2)?->?{
????????list1.addAll(list2);
????????return?list1;
????};
????Function,?List>?finisher?=?(list)?->?list;
????Set?characteristics?=?Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
?
????return?new?Collector,?List>()?{
????????@Override
????????public?Supplier?supplier()?{
????????????return?supplier;
????????}
?
????????@Override
????????public?BiConsumer?accumulator()?{
????????????return?accumulator;
????????}
?
????????@Override
????????public?BinaryOperator?combiner()?{
????????????return?combiner;
????????}
?
????????@Override
????????public?Function?finisher()?{
????????????return?finisher;
????????}
?
????????@Override
????????public?Set?characteristics()? {
????????????return?characteristics;
????????}
????};
?
}
程序汪資料鏈接
程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理
Java項(xiàng)目分享 最新整理全集,找項(xiàng)目不累啦 06版
堪稱神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門到實(shí)戰(zhàn)進(jìn)階
臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開(kāi)放下載!
臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開(kāi)放下載!
字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開(kāi)放下載!
歡迎添加程序汪個(gè)人微信 itwang009? 進(jìn)粉絲群或圍觀朋友圈
