ArrayList擴容分析

ArrayList可以實現容量的自適應的增加(As elements are added to an ArrayList,
its capacity grows automatically)。通過JDK1.8中ArrayList的源碼來分析:

ArrayList的相關定義

ArrayList底層采用Object類型的數組實現。

首先,定義默認初始容量,為10

private static final int DEFAULT_CAPACITY = 10;

再有,定義用于空實例的空數組實例

private static final Object[] EMPTY_ELEMENTDATA = {};

再有,定義空數組實例,用于默認大小的空實例

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

再有,定義了一個未被序列化的數組elementData,用來存儲ArrayList對象列表

/**
 * The capacity of the ArrayList is the length of this array buffer.
 */
transient Object[] elementData;

再有,定義了ArrayList的size變量

/**
     * The size of the ArrayList (the number of elements it contains).
 */
private int size;

為了實現ArrayList的自動擴容機制,java引進了capacity和size概念,以區別數組的length。capacity是底層數組的長度(length),size是存儲的列表對象的數量,被設置為private的。

ArrayList的初始化

ArrayList有三種方式來初始化,構造方法源碼如下:

1.用指定的初始容量構造一個空列表

/**
     * Constructs an empty list with the specified initial capacity.
     */
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //屬性指向新建長度為初始容量的臨時數組
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+  initialCapacity);
        }
    }

2.使用初始容量10構造一個空列表(無參數構造)

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

3.構造包含指定collection元素的列表,這些元素利用該集合的迭代器按順序返回

如果指定的集合為null,throws NullPointerException。

public ArrayList(Collection<? extends E> c) {

        elementData = c.toArray();//用Collection初始化數組elementData
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

ArrayList擴容

以無參數構造為例,初始數組容量為10。當真正對數組進行添加時,才真正分配容量。即向數組中添加第一個元素時,數組容量擴為10。

1.以add(E e)方法為入口分析

 public boolean add(E e) {
       //添加元素之前,先調用ensureCapacityInternal方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;//添加對象時,自增size
        return true;
    }

2.進入到ensureCapacityInternal方法

ensureCapacityInternal(“確保內部容量”)。

 private void ensureCapacityInternal(int minCapacity) {
        //先判斷數組是否為空
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
           //如果數組為空,將DEFAULT_CAPACITY(為10)和minCapacity中較大的一個賦值給minCapacity
           //則minCapaciy至少為10
        }
        ensureExplicitCapacity(minCapacity);
        //執行ensureExplicitCapacity方法
    }

當空列表add()第1個元素時,調試可知:原本minCapacity為1,在Math.max()方法比較后,minCapacity為10。

但當add()第2個元素時,數組非空,直接進入ensureExplicitCapacity(minCapacity)(此時minCapacity為2)。

3.進入ensureExplicitCapacity方法

無論數組是否為空,都會進入ensureExplicitCapacity方法。

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//定義于ArrayList的父類AbstractList,用于存儲結構修改次數
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
        //如果數組的長度(elementData.length)小于最小需要的容量(minCapacity),就擴容
            grow(minCapacity);
    }

當要add第1個元素時,這時elementData.length(為0,因為此時還是空列表)是要比minCapacity(為10)小的,會進入grow(minCapacity)方法(minCapacity為10)。

當add第2個元素時,調試可知:minCapacity為2,此時elementData.length(即是容量)在添加第一個元素后擴容成10了,比minCapacity大,不會去執行grow方法 。數組容量仍為10。

所以當添加第3、4···到第10個元素時,依然不會執行grow方法,數組容量都為10。

直到添加第11個元素,minCapacity(為11)比elementData.length(為10)要大。進入grow方法進行擴容。

4.進入grow方法進行擴容

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//用來分配數組的size最大值
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //右移一位相當于原數除以2,位運算的計算方式更快
        //所以新容量擴大為原容量的1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //從minCapacity和這個newCapacity中取較大值作為擴容后的新數組長度(新容量)。
        //這主要是針對添加第1個元素。1.5倍oldCapacity為0,所以newCapacity為最小容量10
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity); 
         //如果新容量大于數組的最大size,進入hugeCapacity方法
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
        //最后,將原來數組的數據復制到新的數組中。
    
    }

當add第1個元素時,oldCapacity為0,經比較后第一個if判斷成立,newCapacity = minCapacity(為10)。但是第二個if判斷不會成立,即newCapacity 不比 MAX_ARRAY_SIZE大,則不會進入hugeCapacity方法。數組容量為10,add方法中return true,size增為1。

當add第11個元素進入grow方法時,newCapacity為15,比minCapacity(為11)大,第一個if判斷不成立。新容量沒有大于數組最大size,不會進入hugeCapacity方法。數組容量擴為15,add方法中return true,size增為11。

以此類推······

5.如果新容量大于MAX_ARRAY_SIZE,進入hugeCapacity方法

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
            //對minCapacity和MAX_ARRAY_SIZE進行比較
            //若minCapacity大,將Integer.MAX_VALUE作為新數組的大小
            //若MAX_ARRAY_SIZE大,將MAX_ARRAY_SIZE作為新數組的大小
    }

所以,像ArrayList添加對象時,在確定好新數組的大小后,會調用Arrays.copyOf()方法,以適當長度(newCapacity)新建一個原數組的拷貝,并修改原數組,指向這個新建數組。java垃圾回收機制會自動回收原數組。這樣會消耗一定的資源。

因此,在初始化ArrayList時,最好可以估算一個初始大小。

6.最好在add大量元素之前用ensureCapacity方法,以減少增量從新分配的次數。 源碼如下:

/**
 * <p>An application can increase the capacity of an <tt>ArrayList</tt> instance
 * before adding a large number of elements using the <tt>ensureCapacity</tt>
 * operation.  This may reduce the amount of incremental reallocation.
 */
 
     public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

參考:

ArrayList自動擴容解析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容

  • 文章有點長,比較啰嗦,請耐心看完!(基于Android API 25) 一、概述 首先得明白ArrayList在數...
    JerryloveEmily閱讀 3,233評論 2 15
  • 一、基本數據類型 注釋 單行注釋:// 區域注釋:/* */ 文檔注釋:/** */ 數值 對于byte類型而言...
    龍貓小爺閱讀 4,288評論 0 16
  • ArrayList是在Java中最常用的集合之一,其本質上可以當做是一個可擴容的數組,可以添加重復的數據,也支持隨...
    ShawnIsACoder閱讀 576評論 4 7
  • 一、對于ArrayList需要掌握的七點內容 ArrayList的創建:即構造器 往ArrayList中添加對象:...
    rochuan閱讀 894評論 0 0
  • 新的一年,新的開始。每年的一月份總是特別的一個月,結婚的人們總是挑選一月份各種各樣的好時機,預示有一個新的開始,婚...
    蘇穆涼閱讀 395評論 0 0