1. 程式人生 > >for-forEach-stream三種遍歷方法執行效率比較

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,這又是程式設計界的另一大進步,這意味著我們在編寫程式時沒有必要再關注於資料型別,而該資料型別是由相應的語言在執行時確定,這樣,這又一次降低了程式的執行速度,但是相應的又提升了程式碼編寫的效率,因而通過回顧歷史我們不難得出如下結論:

  在編寫程式碼時,一定要以最簡潔為原則,畢竟執行程式的硬體成本會隨著時間的推移在不斷降低,而程式設計師的薪資則不會。