目錄
2、filter / map / flatMap /peek
3、mapToObj / mapToLong / mapToDouble / asLongStream / asDoubleStream
6、distinct / sorted / limit / skip
7、sum / min / max / count / average / summaryStatistics
8、anyMatch / allMatch / noneMatch
Java8支持的流處理的元素類型只有4種,double、int,long和reference類型,可參考AbstractPipeline的類繼承關係,如下:
AbstractPipeline有4個子類,分別表示流處理元素爲double、int,long和reference類型的管道;這4個子類都是抽象類,每個子類下都有3個靜態內部類的實現類,Head、StatefulOp、StatelessOp,其中Head用於創建一個全新的流,StatefulOp表示有狀態的一類操作,StatelessOp表示無狀態的一類操作,這裏的有狀態是指前面流元素的處理會直接影響後面流元素的處理,多線程並行處理下每次運行的結果都不相同。前面3個AbstractPipeline子類的用法和實現是基本一致的,本篇博客以IntPipeline爲例來說明其實現接口IntStream 的使用細節,下篇博客詳細說明其實現細節,該類的類繼承關係如下:
其中IntStream接口定義了int流支持的所有操作。
1、創建int流
創建int流都是由IntStream接口類中的靜態方法完成的,具體如下:
- of / builder: 可指定int流中包含的具體單個元素
- range / rangeClosed : 將指定範圍內的元素都添加到int流中,前者不包含最後一個元素,後者包含
- generate / iterate : 指定生成int流中int元素的生成函數,前者的生成函數沒有入參,後者會將前一次調用結果作爲下一次調用生成函數的入參
- concat :將兩個int流合併成一個
各方法的使用細節可參考如下測試用例:
@Test
public void test() throws Exception {
//包含指定的元素
// IntStream intStream=IntStream.of(1);
//返回的int流中的元素是已經排序好的
IntStream intStream=IntStream.of(1,3,2,5,4,6);
print("of",intStream);
//從11到16,不包含16
intStream=IntStream.range(11,16);
//從11到16,包含16
// intStream=IntStream.rangeClosed(11,16);
print("range",intStream);
//包含指定的元素,add方法底層也是調用accept方法,然後返回this
//返回的int流中的元素順序與添加順序一致
intStream=IntStream.builder().add(23).add(22).add(21).build();
print("builder", intStream);
//指定一個int生成函數
//返回的int流中的元素不排序
intStream=IntStream.generate(()->{
Random random=new Random();
return random.nextInt(100);
}).limit(6);
print("generate", intStream);
//指定一個int生成函數,前一次執行函數的結果會作爲下一次調用函數的入參
//第一個參數seed就是第一次調用生成函數的入參
//返回的int流中的元素不排序
intStream=IntStream.iterate(1,x->{
int a=2*x;
if(a>16){
return a-20;
}else{
return a;
}
}).limit(6);
print("iterate", intStream);
}
@Test
public void test2() throws Exception {
IntStream streamA=IntStream.range(11,15);
IntStream streamB=IntStream.range(6,10);
//將兩個IntStream 合併起來
//返回的int流的元素順序與添加的流的元素順序一致,不排序
IntStream streamC=IntStream.concat(streamA,streamB);
print("concat", streamC);
}
private void print(String start, IntStream intStream){
System.out.println("print for->"+start);
intStream.forEach(x->{
System.out.println(x);
});
}
上述方法的底層實現最終都是調用StreamSupport.intStream(Spliterator.OfInt spliterator, boolean parallel)方法,如下圖:
2、filter / map / flatMap /peek
filter方法會將filter函數返回false的元素從流中去除,只保留filter函數返回true的元素;map方法用於對流中的所有元素執行某個修改動作;peek方法通常用於打印流中的元素,peek函數無返回值;flatMap方法同map方法,區別在於flatMap函數的返回值是一個IntStream 而非int值,可以在返回的IntStream中包含多個元素,flatMap方法最終返回的IntStream是將每次調用flatMap函數返回的IntStream 合併後的結果。其測試用例如下:
@Test
public void test3() throws Exception {
IntStream intStream=IntStream.rangeClosed(1, 10);
//會保留過濾函數返回true的元素,此處是保留偶數
intStream=intStream.filter(x->{
return x%2==0;
}).peek(x->{ //peek方法指定的函數,以流中的元素爲入參,無返回值,即不會修改元素本身
System.out.println("filter->"+x);
});
//對流中的所有元素執行某個修改動作,此處是將所有值加1
intStream=intStream.map(x->{
return x+1;
}).peek(x->{
System.out.println("map->"+x);
});
//flatMap同map,區別在於flatMap指定的函數其返回值是一個IntStream,而非一個int值,最終flatMap返回的
//IntStream是將每次調用flatMap返回的子IntStream合併後的結果
intStream=intStream.flatMap(x->{
//返回IntStream時可以返回多個元素
return IntStream.of(x+3,x+2,x+1);
}).peek(x->{
System.out.println("flatMap->"+x);
});
print("after flatMap", intStream);
}
其輸出如下:
print for->after flatMap
filter->2
map->3
flatMap->6
6
flatMap->5
5
flatMap->4
4
filter->4
map->5
flatMap->8
8
flatMap->7
7
flatMap->6
6
filter->6
map->7
flatMap->10
10
flatMap->9
9
flatMap->8
8
filter->8
map->9
flatMap->12
12
flatMap->11
11
flatMap->10
10
filter->10
map->11
flatMap->14
14
flatMap->13
13
flatMap->12
12
其中因爲flatMap函數返回的IntStream中包含了3個元素,所以peek方法執行了3次,每次都打印三個flatMap。從上述輸出可知,只有開始執行forEach方法後才真正執行流處理動作,且原始的intStream只遍歷一遍,每次遍歷一個元素就按照代碼聲明的順序依次執行各個處理動作。
3、mapToObj / mapToLong / mapToDouble / asLongStream / asDoubleStream
這幾個方法都是將int流轉換成其他類型的流,mapTo的三個方法可以指定具體的轉換函數,as的兩個方法不能指定,遵循標準的int到指定類型的轉換規則,其實現如下:
@Test
public void test4() throws Exception {
IntStream intStream=IntStream.rangeClosed(1, 3);
intStream.mapToObj(x->{
return new Age(x);
}).forEach(x->{
System.out.println("mapToObj->"+x);
});
//執行完mapToObj後intStream本身已經關閉了不能繼續操作,只能操作其返回的新流
//此處要繼續操作就必須重新初始化一個新的IntStream
intStream=IntStream.rangeClosed(1, 3);
intStream.mapToLong(x->{
return x+1;
}).forEach(x->{
System.out.println("mapToLong->"+x);
});
intStream=IntStream.rangeClosed(1, 3);
intStream.mapToDouble(x->{
return x+2;
}).forEach(x->{
System.out.println("mapToDouble->"+x);
});
//同上面的mapToLong,區別在於不能指定轉換函數,而是採用標準的int到long類型的轉換方法
intStream=IntStream.rangeClosed(1, 3);
intStream.asLongStream().forEach(x->{
System.out.println("asLongStream->"+x);
});
intStream=IntStream.rangeClosed(1, 3);
intStream.asDoubleStream().forEach(x->{
System.out.println("asDoubleStream->"+x);
});
}
其輸出如下:
4、forEach / forEachOrdered
這兩個方法都是用來遍歷流中的元素,跟peek方法不同的是該這兩個方法的返回值是void,執行此類返回值爲void的方法會觸發實際的流處理動作。forEachOrdered同forEach的區別在於並行流處理下,forEachOrdered會保證實際的處理順序與流中元素的順序一致,而forEach方法無法保證,默認的串行流處理下,兩者無區別,都能保證處理順序與流中元素順序一致,測試用例如下:
@Test
public void test6() throws Exception {
IntStream intStream=IntStream.of(6,1,3,2,5,4).parallel();
intStream.forEach(x->{
System.out.println("forEach->"+x);
});
//forEachOrdered同forEach,區別在於並行流處理下,forEachOrdered會保證實際的處理順序與流中元素的順序一致
//而forEach方法無法保證,默認的串行流處理下,兩者無區別,都能保證處理順序與流中元素順序一致
intStream=IntStream.of(6,1,3,2,5,4).parallel();
intStream.forEachOrdered(x->{
System.out.println("forEachOrdered->"+x);
});
}
其輸出如下:
5、 reduce / collect
reduce方法用於執行類似於累加的操作,上一次調用處理函數的結果會作爲入參下一次調用的入參;collect方法的效果跟forEach類似,注意其返回值是調用supplier函數的返回值,該函數在整個流處理過程中只調用一次,參考如下測試用例:
@Test
public void test7() throws Exception {
IntStream intStream=IntStream.of(6,1,3,2,5,4);
OptionalInt optionalInt=intStream.reduce((x, y)->{
System.out.println("x->"+x+",y->"+y);
return x+y;
});
System.out.println("result->"+optionalInt.getAsInt());
System.out.println("");
intStream=IntStream.of(6,1,3,2,5,4);
//同第一個reduce方法,區別在於可以指定起始的left,第一個reduce方法使用第一個元素作爲起始的left
int result=intStream.reduce(2,(x, y)->{
System.out.println("x->"+x+",y->"+y);
return x+y;
});
System.out.println("result->"+result+"\n");
intStream=IntStream.of(6,1,3,2,5,4);
//同forEach方法,首先調用supplier函數生成一個值,將該值作爲accumulator函數的第一個參數,accumulator函數的第二個
//參數就是流中的元素,注意第三個參數combiner無意義,可置爲null
result=intStream.collect(()->{
Random random=new Random();
return random.nextInt(10);
},(x,y)->{
System.out.println("ObjIntConsumer x->"+x+",y->"+y);
},null);
//返回值是supplier函數生成的值
System.out.println("collect result->"+result+"\n");
}
其輸出如下:
6、distinct / sorted / limit / skip
distinct用於對流中的元素去重,sorted用於對流中的元素升序排序,limit用於限制流中元素的個數,多餘的元素會被丟棄,skip用於跳過前面指定N個元素,參考如下測試用例:
@Test
public void test8() throws Exception {
IntStream intStream=IntStream.of(6,1,1,2,5,2,3,3,4,8,6,11,10,9);
intStream.distinct() //對流中的元素去重
.sorted() //將流中的元素排序,默認升序
.skip(3) //跳過前3個元素,此處是跳過1,2,3三個元素
.limit(6) //限制流中元素的最大個數
.forEach(x->{
System.out.println(x);
});
}
其輸出如下:
7、sum / min / max / count / average / summaryStatistics
前面5個方法分別是獲取流中元素的總和,最小值,最大值,個數和平均值,最後一個summaryStatistics方法是一次調用獲取上述屬性。測試用例如下:
@Test
public void test9() throws Exception {
IntStream intStream=IntStream.of(6,1,1,2,5,2,3,4);
//取流中元素的最大值
OptionalInt max=intStream.max();
System.out.println("max->"+max.getAsInt());
//同其他沒有流的方法,max操作會中斷流,再對該流執行相關流處理方法會報錯synchronizedTest.StreamTest
intStream=IntStream.of(6,1,1,2,5,2,3,4);
//取流中元素的最小值
OptionalInt min=intStream.min();
System.out.println("max->"+max.getAsInt());
intStream=IntStream.of(6,1,1,2,5,2,3,4);
//取流中元素的平均值
OptionalDouble average=intStream.average();
System.out.println("average->"+average.getAsDouble());
intStream=IntStream.of(6,1,1,2,5,2,3,4);
//取流中元素的個數
long count=intStream.count();
System.out.println("count->"+count);
intStream=IntStream.of(6,1,1,2,5,2,3,4);
//取流中元素的總和
int sum=intStream.sum();
System.out.println("sum->"+sum);
intStream=IntStream.of(6,1,1,2,5,2,3,4);
//取流中元素的統計情形,即一次返回min,max,count等屬性
IntSummaryStatistics summaryStatistics=intStream.summaryStatistics();
System.out.println(summaryStatistics.toString());
}
其輸出如下:
8、anyMatch / allMatch / noneMatch
有任何一個元素匹配,anyMatch返回true;所有元素都匹配時,allMatch返回true;沒有一個元素匹配時,noneMatch返回true,參考如下測試用例:
@Test
public void test10() throws Exception {
IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//有任何一個匹配,返回true
boolean anyMatch=intStream.anyMatch(x->{
return x%2==0;
});
System.out.println("anyMatch->"+anyMatch);
intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//所有的都匹配,返回true
boolean allMatch=intStream.allMatch(x->{
return x%2==0;
});
System.out.println("allMatch->"+allMatch);
intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//所有的都不匹配,返回true
boolean noneMatch=intStream.noneMatch(x->{
return x%2==0;
});
System.out.println("noneMatch->"+noneMatch);
}
輸出如下:
9、findFirst / findAny
findFirst返回流中第一個元素,findAny返回流中的任意一個元素,參考如下用例:
@Test
public void test11() throws Exception {
IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//返回第一個元素
OptionalInt result=intStream.findFirst();
System.out.println("findFirst->"+result.getAsInt());
for(int i=0;i<6;i++) {
intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//返回任意一個元素
result = intStream.findAny();
System.out.println("findAny->" + result.getAsInt());
}
}
10、sequential / parallel
sequential 返回的流是串行處理,parallel返回的流是並行處理,參考如下測試用例:
@Test
public void test12() throws Exception {
IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
long start=System.currentTimeMillis();
//並行處理
intStream.parallel().forEach(x->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+x);
});
System.out.println("parallel time->"+(System.currentTimeMillis()-start));
intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
start=System.currentTimeMillis();
//默認都是串行處理
intStream.sequential().forEach(x->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+x);
});
System.out.println("sequential time->"+(System.currentTimeMillis()-start));
}
其輸出如下:
從輸出可知parallel返回的流起了4個線程來處理,主線程和3個commonPool線程,這4個線程並不是嚴格的從一開始就並行執行,因爲其執行總耗時是2061ms,而不是1s左右;串行處理是各個元素的處理耗時累加起來,8個元素,剛好是8s。
11、iterator / boxed / toArray
iterator 返回一個元素遍歷器實現,boxed 返回一個流中元素的包裝類的流,可用mapToObj實現相同的功能,toArray將流中的元素作爲一個數組返回,參考如下測試用例:
@Test
public void test13() throws Exception {
IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//返回一個包裝類的流,效果相當於下面的mapToObj方法
// Stream<Integer> integerStream=intStream.boxed();
Stream<Integer> integerStream=intStream.mapToObj(x->{
return new Integer(x);
});
integerStream.forEach(x->{
System.out.println("boxed->"+x);
});
//返回一個元素遍歷器,PrimitiveIterator是繼承自Iterator
intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
PrimitiveIterator.OfInt iterator=intStream.iterator();
while (iterator.hasNext()){
System.out.println("iterator->"+iterator.next());
}
//將流中的元素轉換成一個數組
intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
int[] results=intStream.toArray();
System.out.println("toArray->"+Arrays.toString(results));
}
其輸出如下:
12、close / onClose
close方法會關閉流,並觸發所有的onClose方法的執行;onClose方法用於註冊一個回調函數,該方法返回一個新的流,可以連續調用註冊多個回調函數,close觸發時會按照註冊的順序依次執行。參考如下測試用例:
@Test
public void test14() throws Exception {
IntStream intStream = IntStream.of(6, 1, 1, 2, 5, 2, 3, 4);
//onClose方法的返回值是一個新的流,可以連續調用onClose,註冊多個回調方法
intStream.onClose(()->{
System.out.println("intStream isClosed one ");
}).onClose(()->{
System.out.println("intStream isClosed two");
}).onClose(()->{
System.out.println("intStream isClosed three");
});
//觸發onClose方法註冊的多個回調方法的執行,並關閉流
intStream.close();
//流已關閉,不能執行流處理動作,forEach執行完成也會關閉流但是不會觸發onClose方法的執行
// intStream.forEach(x->{
// System.out.println(x);
// });
System.out.println("main end");
}
其輸出如下: