JDK8 Stream 數(shù)據(jù)流效率分析
Stream
是Java SE 8類庫中新增的關(guān)鍵抽象,它被定義于 java.util.stream
(這個包里有若干流類型:Stream
代表對象引用流,此外還有一系列特化流,如 IntStream,LongStream,DoubleStream
等 )。
Java 8 引入的的Stream
主要用于取代部分Collection
的操作,每個流代表一個值序列,流提供一系列常用的聚集操作,可以便捷的在它上面進(jìn)行各種運(yùn)算。集合類庫也提供了便捷的方式使我們可以以操作流的方式使用集合、數(shù)組以及其它數(shù)據(jù)結(jié)構(gòu);
stream 的操作種類
①中間操作
- 當(dāng)數(shù)據(jù)源中的數(shù)據(jù)上了流水線后,這個過程對數(shù)據(jù)進(jìn)行的所有操作都稱為“中間操作”;
- 中間操作仍然會返回一個流對象,因此多個中間操作可以串連起來形成一個流水線;
-
stream
提供了多種類型的中間操作,如filter
、distinct
、map
、sorted
等等;
②終端操作
-
當(dāng)所有的中間操作完成后,若要將數(shù)據(jù)從流水線上拿下來,則需要執(zhí)行終端操作;
-
stream
對于終端操作,可以直接提供一個中間操作的結(jié)果,或者將結(jié)果轉(zhuǎn)換為特定的collection
、array
、String
等;
stream 的特點(diǎn)
①只能遍歷一次:
數(shù)據(jù)流的從一頭獲取數(shù)據(jù)源,在流水線上依次對元素進(jìn)行操作,當(dāng)元素通過流水線,便無法再對其進(jìn)行操作,可以重新在數(shù)據(jù)源獲取一個新的數(shù)據(jù)流進(jìn)行操作;
②采用內(nèi)部迭代的方式:
對Collection
進(jìn)行處理,一般會使用 Iterator
遍歷器的遍歷方式,這是一種外部迭代;
而對于處理Stream,只要申明處理方式,處理過程由流對象自行完成,這是一種內(nèi)部迭代,對于大量數(shù)據(jù)的迭代處理中,內(nèi)部迭代比外部迭代要更加高效;
stream 相對于 Collection 的優(yōu)點(diǎn)
注無存儲: 流并不存儲值;流的元素源自數(shù)據(jù)源(可能是某個數(shù)據(jù)結(jié)構(gòu)、生成函數(shù)或I/O通道等等),通過一系列計算步驟得到;
- 函數(shù)式風(fēng)格: 對流的操作會產(chǎn)生一個結(jié)果,但流的數(shù)據(jù)源不會被修改;
- 惰性求值: 多數(shù)流操作(包括過濾、映射、排序以及去重)都可以以惰性方式實(shí)現(xiàn)。這使得我們可以用一遍遍歷完成整個流水線操作,并可以用短路操作提供更高效的實(shí)現(xiàn);
-
無需上界: 不少問題都可以被表達(dá)為無限流(
infinite stream
):用戶不停地讀取流直到滿意的結(jié)果出現(xiàn)為止(比如說,枚舉 完美數(shù) 這個操作可以被表達(dá)為在所有整數(shù)上進(jìn)行過濾);集合是有限的,但流可以表達(dá)為無線流; -
代碼簡練: 對于一些
collection
的迭代處理操作,使用 stream 編寫可以十分簡潔,如果使用傳統(tǒng)的collection
迭代操作,代碼可能十分啰嗦,可讀性也會比較糟糕;
stream 和 iterator 迭代的效率比較
好了,上面 stream
的優(yōu)點(diǎn)吹了那么多,stream
函數(shù)式的寫法是很舒服,那么 steam
的效率到底怎樣呢?
先說結(jié)論:
-
傳統(tǒng)
iterator (for-loop)
比stream(JDK8)
迭代性能要高,尤其在小數(shù)據(jù)量的情況下; -
在多核情景下,對于大數(shù)據(jù)量的處理,
parallel stream
可以有比iterator
更高的迭代處理效率;
我分別對一個隨機(jī)數(shù)列 List (數(shù)量從 10 到 10000000)進(jìn)行映射、過濾、排序、規(guī)約統(tǒng)計、字符串轉(zhuǎn)化場景下,對使用 stream 和 iterator 實(shí)現(xiàn)的運(yùn)行效率進(jìn)行了統(tǒng)計。
測試環(huán)境如下:
System:Ubuntu 16.04 xenial
CPU:Intel Core i7-8550U
RAM:16GB
JDK version:1.8.0_151
JVM:HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
JVMSettings:
-Xms1024m
-Xmx6144m
-XX:MaxMetaspaceSize=512m
-XX:ReservedCodeCacheSize=1024m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=100
1. 映射處理測試
把一個隨機(jī)數(shù)列(List
)中的每一個元素自增1后,重新組裝為一個新的 List
,測試的隨機(jī)數(shù)列容量從 10 - 10000000
,跑10次取平均時間;
//stream
Listresult=list.stream()
.mapToInt(x->x)
.map(x->++x)
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
//iterator
Listresult=newArrayList<>();
for(Integere:list){
result.add(++e);
}
//parallelstream
Listresult=list.parallelStream()
.mapToInt(x->x)
.map(x->++x)
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
2. 過濾處理測試
取出一個隨機(jī)數(shù)列(List
)中的大于 200 的元素,并組裝為一個新的 List
,測試的隨機(jī)數(shù)列容量從 10 - 10000000
,跑10次取平均時間;
//stream
Listresult=list.stream()
.mapToInt(x->x)
.filter(x->x>200)
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
//iterator
Listresult=newArrayList<>(list.size());
for(Integere:list){
if(e>200){
result.add(e);
}
}
//parallelstream
Listresult=list.parallelStream()
.mapToInt(x->x)
.filter(x->x>200)
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
3. 自然排序測試
對一個隨機(jī)數(shù)列(List
)進(jìn)行自然排序,并組裝為一個新的 List
,iterator
使用的是 Collections # sort API
(使用歸并排序算法實(shí)現(xiàn)),測試的隨機(jī)數(shù)列容量從 10 - 10000000
,跑10次取平均時間;
//stream
Listresult=list.stream()
.mapToInt(x->x)
.sorted()
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
//iterator
Listresult=newArrayList<>(list);
Collections.sort(result);
//parallelstream
Listresult=list.parallelStream()
.mapToInt(x->x)
.sorted()
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
4. 歸約統(tǒng)計測試
獲取一個隨機(jī)數(shù)列(List
)的最大值,測試的隨機(jī)數(shù)列容量從 10 - 10000000,跑10次取平均時間;
//stream
intmax=list.stream()
.mapToInt(x->x)
.max()
.getAsInt();
//iterator
intmax=-1;
for(Integere:list){
if(e>max){
max=e;
}
}
//parallelstream
intmax=list.parallelStream()
.mapToInt(x->x)
.max()
.getAsInt();
5. 字符串拼接測試
獲取一個隨機(jī)數(shù)列(List
)各個元素使用“,”分隔的字符串,測試的隨機(jī)數(shù)列容量從 10 - 10000000
,跑10次取平均時間;
//stream
Stringresult=list.stream().map(String::valueOf).collect(Collectors.joining(","));
//iterator
StringBuilderbuilder=newStringBuilder();
for(Integere:list){
builder.append(e).append(",");
}
Stringresult=builder.length()==0?"":builder.substring(0,builder.length()-1);
//parallelstream
Stringresult=list.stream().map(String::valueOf).collect(Collectors.joining(","));
6. 混合操作測試
對一個隨機(jī)數(shù)列(List
)進(jìn)行去空值,除重,映射,過濾,并組裝為一個新的 List
,測試的隨機(jī)數(shù)列容量從 10 - 10000000
,跑10次取平均時間;
//stream
Listresult=list.stream()
.filter(Objects::nonNull)
.mapToInt(x->x+1)
.filter(x->x>200)
.distinct()
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
//iterator
HashSetset=newHashSet<>(list.size());
for(Integere:list){
if(e!=null&&e>200){
set.add(e+1);
}
}
Listresult=newArrayList<>(set);
//parallelstream
Listresult=list.parallelStream()
.filter(Objects::nonNull)
.mapToInt(x->x+1)
.filter(x->x>200)
.distinct()
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
實(shí)驗(yàn)結(jié)果總結(jié)
從以上的實(shí)驗(yàn)來看,可以總結(jié)處以下幾點(diǎn):
-
在少低數(shù)據(jù)量的處理場景中(
size<=1000
),stream
的處理效率是不如傳統(tǒng)的iterator
外部迭代器處理速度快的,但是實(shí)際上這些處理任務(wù)本身運(yùn)行時間都低于毫秒,這點(diǎn)效率的差距對普通業(yè)務(wù)幾乎沒有影響,反而stream
可以使得代碼更加簡潔; -
在大數(shù)據(jù)量(
szie>10000
)時,stream
的處理效率會高于iterator
,特別是使用了并行流,在cpu恰好將線程分配到多個核心的條件下(當(dāng)然parallel stream
底層使用的是 JVM 的ForkJoinPool
,這東西分配線程本身就很玄學(xué)),可以達(dá)到一個很高的運(yùn)行效率,然而實(shí)際普通業(yè)務(wù)一般不會有需要迭代高于10000次的計算; -
Parallel Stream
受引 CPU 環(huán)境影響很大,當(dāng)沒分配到多個cpu核心時,加上引用forkJoinPool
的開銷,運(yùn)行效率可能還不如普通的Stream
;
使用 Stream 的建議
-
簡單的迭代邏輯,可以直接使用
iterator
,對于有多步處理的迭代邏輯,可以使用stream
,損失一點(diǎn)幾乎沒有的效率,換來代碼的高可讀性是值得的; -
單核 cpu 環(huán)境,不推薦使用
parallel stream
,在多核 cpu 且有大數(shù)據(jù)量的條件下,推薦使用paralle stream
; -
stream
中含有裝箱類型,在進(jìn)行中間操作之前,最好轉(zhuǎn)成對應(yīng)的數(shù)值流,減少由于頻繁的拆箱、裝箱造成的性能損失;
審核編輯:湯梓紅
-
JAVA
+關(guān)注
關(guān)注
20文章
2982瀏覽量
106392 -
Stream
+關(guān)注
關(guān)注
0文章
21瀏覽量
8065 -
數(shù)據(jù)流
+關(guān)注
關(guān)注
0文章
122瀏覽量
14621
原文標(biāo)題:Java8 Stream 遍歷數(shù)據(jù)效率差?實(shí)測結(jié)果出乎意料~~
文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
大眾數(shù)據(jù)流分析
基于數(shù)據(jù)流分析與識別的Web資源訪問控制
基于數(shù)據(jù)流的Java字節(jié)碼分析
網(wǎng)絡(luò)數(shù)據(jù)流存儲算法分析與實(shí)現(xiàn)

基于FPGA芯片的數(shù)據(jù)流結(jié)構(gòu)分析
數(shù)據(jù)流是什么
如何解決JDK8小版本升級后性能下降的問題

Java8的Stream流 map() 方法

JDK8升級JDK11最全實(shí)踐干貨來了

評論