驗證ArrayList插入一樣數據使用指定容量和默認容量的效率

驗證ArrayList插入一樣數據使用指定容量和默認容量的效率

以前在研究ArrayList源碼的時候看到過一篇文章Java 8 容器源碼-ArrayList裏面說當ArrayList在進行插入的時候,若是容量不夠那麼就會進行自動擴容,擴容大小是現有容量的1.5倍,具體代碼能夠參考下面。html

此處的默認容量是指當構建空的ArrayList構造函數時給分配的默認數組容量大小,爲10。java

/**
    * 擴容,保證ArrayList至少能存儲minCapacity個元素
    * 第一次擴容,邏輯爲newCapacity = oldCapacity + (oldCapacity >> 1);即在原有的容量基礎上增長一半。第一次擴容後,若是容量仍是小於minCapacity,就將容量擴充爲minCapacity。
    *
    * @param minCapacity 想要的最小容量
    */
    private void grow(int minCapacity) {
        // 獲取當前數組的容量
        int oldCapacity = elementData.length;
        // 擴容。新的容量=當前容量+當前容量/2.即將當前容量增長一半。
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //若是擴容後的容量仍是小於想要的最小容量
        if (newCapacity - minCapacity < 0)
            //將擴容後的容量再次擴容爲想要的最小容量
            newCapacity = minCapacity;
        //若是擴容後的容量大於臨界值,則進行大容量分配
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData,newCapacity);
    }
    /**

複製代碼

因此在網上就會有許多的人說當知道了要用ArrayList存儲多少數據的前提下,咱們要指定ArrayList的容量大小,這樣會減小ArrayList自動擴容的次數而增長效率。程序員

此時相信你們內心就會和我有同樣的疑問,在不一樣數據量的面前使用默認容量和指定容量大小效率到底會有多少的差異。相信有些人(好比我)一開始上來就會寫以下的代碼進行驗證。編程

public class TestListAdd {

    public static void main(String[] args){

        System.out.println("Test 10000000 List add start");

        System.out.println("Default Capacity: "+ listAdd(10000000));//默認容量

        System.out.println("10000000 Capacity: "+ listAdd(10000000,10000000));//指定容量
        
    }

    public static Long listAdd(int num){
        Long starTime = System.currentTimeMillis();
        List<Object> list = new ArrayList<>();
        for (Integer i = 0; i < num; i++) {
            list.add(i);
        }
        Long endTime = System.currentTimeMillis();
        return endTime - starTime;
    }

    public static Long listAdd(int capatity,int num){
        Long starTime = System.currentTimeMillis();
        List<Object> list = new ArrayList<>(capatity);
        for (Integer i = 0; i < 10000000; i++) {
            list.add(i);
        }
        Long endTime = System.currentTimeMillis();
        return endTime - starTime;
    }
}

複製代碼

結果固然也是符合預期的,下面的輸出比上面的要少量多數組

Test 10000000 List add start
Default Capacity: 2416
10000000 Capacity: 397


複製代碼

若是試驗到此爲止我也就不會寫這篇博客了,由於沒什麼可寫的了,可是當我將上面默認容量大小和指定容量大小兩行代碼換個位置,那麼結果就會與咱們所想的不相同。bash

Test 10000000 List add start
10000000 Capacity: 2130
Default Capacity: 933

複製代碼

通過查詢各類資料,中間走了許多的坑,例如一開始我想着是第一次的調用會將int從0-10000000的數第一次建立到常量池裏面,而第二次就不用建立直接去常量池裏面去取第一次建立好的int類型就行了,因此每次的第一次調用會比第二次調用速度要慢許多。固然隨後通過學習查資料,否認了我第一次的蠢想法。兩個不一樣的方法內的變量根本不會有交互。隨後看到了一篇文章和個人問題差很少java循環長度的相同、循環體代碼相同的兩次for循環的執行時間相差了100倍?裏面的回答提到了一句話。併發

在HotSpot VM上跑microbenchmark切記不要在main()裏跑循環計時就完事。這是典型錯誤。重要的事情重複三遍:請用JMH,請用JMH,請用JMH。除非很是瞭解HotSpot的實現細節,在main裏這樣跑循環計時獲得的結果其實對通常程序員來講根本沒有任何意義,由於沒法解釋。框架

固然裏面的其餘術語我是看不懂的,只是簡單的明白了兩點函數

  1. 使用main方法進行微測一個方法的執行效率是不對的。具體怎麼不對,我也不清楚,看不懂,裏面的答案有說明。
  2. 要使用JMH、要使用JMH、要使用JMH。

JMH 是 Java Microbenchmark Harness(微基準測試)框架的縮寫(2013年首次發佈)。與其餘衆多測試框架相比,其特點優點在於它是由 Oracle 實現 JIT 的相同人員開發的。性能

我會在後面貼出一些學習JMH的經典文章。下面就開始咱們的測試吧。

測試準備

使用JMH進行測試十分的簡單,只須要加入一些註解便可。他就能屏蔽一些JVM對於測試的影響。讓咱們只關注於測試結果。

@Benchmark
    public static void listAdd(Blackhole blackhole){
        List<Object> list = new ArrayList<>(10000);
        for (Integer i = 0; i < 10000; i++) {
            list.add(i);
        }
        blackhole.consume(list);
    }

複製代碼

個人JMH的配置以下

  • warmup iterations的數量: 10
  • iterations的數量:10
  • forks的數量:2
  • threads的數量:1
  • 時間:TimeUnit.NANOSECONDS
  • Mode:Mode.AverageTime

具體的測試代碼我就不展現了,其中無非就是循環次數和初始化的容量。我就直接將數據展現出來吧。

循環次數 初始化容量 平均時間(微秒)
10 10 55.38
100 79.06
1000 361.85
10000 2355.82
循環次數 初始化容量 平均時間(微秒)
100 10 524.50
100 506.01
1000 851.23
10000 2804.22
循環次數 初始化容量 平均時間(微秒)
1000 10 7233.56
100 6923.32
1000 5284.91
10000 7723.32
循環次數 初始化容量 平均時間(微秒)
10000 10 81188.92
100 64393.20
1000 59316.76
10000 51382.20

下面是根據上面數據畫的折線圖,可以更加直觀的感覺到變化。

總結

大概由上面的試驗,咱們可以得出如下結論

  • 插入總數必定時,當初始化容量<插入總數。那麼初始化容量越大效率越高。
  • 插入總數必定時,當初始化容量>插入總數。那麼初始化容量越大效率越低。

尤爲這種效率的提高在數據量大的時候更爲明顯,由於數據量大而致使初始化容量不夠,擴容次數不斷的增長。致使效率下降。

參考文章

相關文章
相關標籤/搜索