for-forEach-stream三種遍歷方法執行效率比較與選用思考
想法
在JDK-8中,新添加了很多的方法,比如說現在我所說的forEach,該方法是用於集合遍歷的,其方式相當於傳統的for迴圈遍歷方式,只是與其不同之處就在於採用了lambda表示式,因而在進行迴圈遍歷時程式碼更加的簡介。
但是我們知道,在JDK-8中,除了可以採用forEach來進行集合遍歷之外,還可以採用流的形式來對集合進行遍歷操作,比如說stream,因而我就突然想到了比較一下三者執行效率到底如何。
設計
下面就是我對於該一想法的實現,由於以上三種方法中含有流,所以說我們操作的物件就選擇為實體類,因而我建立了一個實體類Demo,在該實體類中一共存放了兩個欄位,分別為index索引值欄位以及str字串欄位。其中index索引值欄位存放的是我們要取用的資訊,而str欄位則是屬於干擾欄位,實際上並沒有取用。接下來我就是建立100萬個Demo,在其中存放相應的資訊,然後再使用不同的方法對其進行遍歷。最後一步就是比較for、forEach與stream中兩兩執行時間差異並由此得出他們的執行效率排序。
實現
Demo中的原始碼:
package com.lyc;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = {"index"})
public class Demo {
private int index;
private String str;
}
TraverseComparisonTest中的原始碼:
package com.lyc;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
public class TraverseComparisonTest {
List<Demo> demoList = null;
List<Integer> traditionList = null;
List<Integer> forEachList = null ;
List<Integer> streamList = null;
/**
* 準備資料
*/
@Before
public void prepareDatabase(){
demoList = Lists.newArrayList();
traditionList = Lists.newArrayList();
forEachList = Lists.newArrayList();
streamList = Lists.newArrayList();
String str = "這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;" +
"這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;" +
"這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;" +
"這個是干擾欄位;這個是干擾欄位;這個是干擾欄位;";
for(int i = 0;i < 1000000;i ++){
Demo demo = new Demo(i,str);
demoList.add(demo);
}
}
/**
* 傳統的方式
* @return
*/
public long traditionMethod(){
Date date = new Date();
int traditionListLength = traditionList.size();
for(int i = 0;i < traditionListLength;i ++){
traditionList.add(demoList.get(i).getIndex());
}
Date date2 = new Date();
return date2.getTime() - date.getTime();
}
/**
* forEach方式
* @return
*/
public long forEachMethod(){
Date date = new Date();
demoList.forEach(demo -> {
forEachList.add(demo.getIndex());
});
Date date2 = new Date();
return date2.getTime() - date.getTime();
}
/**
* stream方式
* @return
*/
public long streamMethod(){
Date date = new Date();
streamList = demoList.stream().map(demo -> demo.getIndex()).collect(Collectors.toList());
Date date2 = new Date();
return date2.getTime() - date.getTime();
}
/**
* 比較stream與傳統的取值方式效率
*/
@Test
public void test(){
long time1 = traditionMethod();
long time2 = forEachMethod();
long time3 = streamMethod();
log.info("for與forEach比較結果為:" + (time1 - time2));
log.info("forEach與stream比較結果為:" + (time2 - time3));
log.info("for與stream比較結果為:" + (time1 - time3));
}
}
執行結果:
2018-03-03 15:20:14 INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:85) - for與forEach比較結果為:-78
2018-03-03 15:20:14 INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:86) - forEach與stream比較結果為:-678
2018-03-03 15:20:14 INFO main com.lyc.TraverseComparisonTest.test(TraverseComparisonTest.java:87) - for與stream比較結果為:-756
結論
通過上述比較,在共同對Demo遍歷100萬次的過程中,for迴圈比forEach快78毫秒,而forEach又比stream快678毫秒,而for比stream快756毫秒。參考程式碼量與執行效率,我們不難得出如下結論:
- 它們的執行效率排序為 :
for > forEach > stream
- 它們的程式碼編寫量排序為:
for < forEach < stream
建議
在編寫程式碼的過程中,我們究竟採用哪種遍歷方式比較好?!這個得有一個選擇標準,如果單純的以執行效率為主,那還是採用傳統的for迴圈,若要簡化程式碼量,那就選用stream。不過我給大家的建議是多使用stream而不是執行效率最高的for迴圈,理由如下:
回顧程式設計的發展歷史,我們不難發現一個規律,那就是先是從最初的C/C++演變到Java/.net,這是程式設計界的一大進步,因為我們不再關注於指標操作,比如在java中JVM虛擬機器已經幫我們完成了相應的操作,由於這一進步,這付出的代價是執行效率會降低,但是帶來的好處就在於加快了程式設計開發的速度。
當程式設計由Java/.net演變到JavaScript/PHP/Kotlin,這又是程式設計界的另一大進步,這意味著我們在編寫程式時沒有必要再關注於資料型別,而該資料型別是由相應的語言在執行時確定,這樣,這又一次降低了程式的執行速度,但是相應的又提升了程式碼編寫的效率,因而通過回顧歷史我們不難得出如下結論:
在編寫程式碼時,一定要以最簡潔為原則,畢竟執行程式的硬體成本會隨著時間的推移在不斷降低,而程式設計師的薪資則不會。