淺談ArrayList的底層擴容的原理


ArrayList擴容機制的源碼詳解

一:ArrayList的構造函數:

ArrayList的構造函數源碼有三種:

先來看看ArrayList底層定義的一些變量的含義:

/**  Default initial capacity
  *  默認的容量大小
  */
private static final int DEFAULT_CAPACITY = 10;

/**  Shared empty array instance used for empty instances.
  *  定義的空的數組
  */
private static final Object[] EMPTY_ELEMENTDATA = {};

 /**
   * The array buffer into which the elements of the ArrayList are stored.
   * The capacity of the ArrayList is the length of this array buffer. Any
   * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
   * will be expanded to DEFAULT_CAPACITY when the first element is added.
   * 不可以被序列化的數組
   */
    transient Object[] elementData; // non-private to simplify nested class access


 /**
   * The size of the ArrayList (the number of elements it contains).
   * 這個list集合的長度
   * @serial
   */
    private int size;
 /**
  * Constructs an empty list with an initial capacity of ten
  * 空的構造函數
  */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    
 /**
   * Constructs an empty list with the specified initial capacity.
   *
   * @param  initialCapacity  the initial capacity of the list
   * @throws IllegalArgumentException if the specified initial capacity
   *         is negative
   * 根據用戶傳入的容量大小構造一個list集合,長度可以大於等於0,但是如果為負數會拋出異常
   */
    public ArrayList(int initialCapacity) {
        // 如果初始容量大於0
        if (initialCapacity > 0) {
            // 創建一個大小為initialCapacity的數組
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            // 創建一個空數組
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
           // 如果為負數,直接拋出異常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    
    
 /**
   * Constructs a list containing the elements of the specified
   * collection, in the order they are returned by the collection's
   * iterator.
   *
   * @param c the collection whose elements are to be placed into this list
   * @throws NullPointerException if the specified collection is null
   * 構造包含指定collection元素的列表,這些元素利用該集合的迭代器按順序返回
   * 如果指定的集合為null,throws NullPointerException。
   */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        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的擴容機制

主要來分析一下無參的構造函數:先來看看add()方法

1:add()方法的實現

 // 將指定的元素加到列表的末尾
 public boolean add(E e) {
        // 添加元素之前,先調用ensureCapacityInternal方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 這里看到的ArrayList添加元素的實質相當於為數組賦值
        elementData[size++] = e;
        return true;
    }

2:ensureCapacityInternal方法的實現

// 得到最小的擴容量
private void ensureCapacityInternal(int minCapacity) {
        // 當一開始是默認空的列表
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 獲取默認的容量和傳入參數的最大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

例如:當add進第一個元素時候,minCapacity為1,此時去二者的最大值

3:比較ensureExplicitCapacity

// 判斷是否需要擴容
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            // 調用grow方法進行擴容
            grow(minCapacity);
    }

我們來將上面的內容梳理一下,會get到幾個點:

*當我們add進第一個元素到ArrayList時, elementData.length 為0 (因為還是一個空的 list) ,但是此時執行了ensureCapacityInternal()方法,通過默認的比較,此時會得到minCapacity為10。此時minCapacity - elementData.length > 0滿足,所以會進入grow(minCapacity)方法.

*當add第二個元素時, minCapacity 為2,此時e lementData.length(容量)在添加第一個元素后擴容成 10 了。此時,minCapacity - elementData.length > 0 不成立,所以不會進入 (執行)grow(minCapacity) 方法。

*同理添加第3,4,5,6.....一直到11個元素都不會grow,當到第11個元素的時候,minCapacity(11)比10大,會進行grow操作

4:grow()方法(擴容核心)

// 需要分配的數組大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
        // overflow-conscious code
        // 集合的容量
        int oldCapacity = elementData.length;
        // 新的集合的容量(在這里運用了位運算,位運算是計算機最快的,右移一位,所以新容量是1.5倍)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新容量小於添加的集合的容量,則把該容量替換
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            
        /** 如果新容量大於 MAX_ARRAY_SIZE,進入(執行) `hugeCapacity()` 方法來比較 minCapacity 和           * MAX_ARRAY_SIZE,如果minCapacity大於最大容量,則新容量則為`Integer.MAX_VALUE`,否則,           * 新容量大小則為 MAX_ARRAY_SIZE 即為 `Integer.MAX_VALUE - 8`。
          */
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // 將原數組copy到新的數組中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    /** 如果新容量大於 MAX_ARRAY_SIZE,進入(執行) `hugeCapacity()` 方法來比較 minCapacity 和           * MAX_ARRAY_SIZE,如果minCapacity大於最大容量,則新容量則為`Integer.MAX_VALUE`,否則,           * 新容量大小則為 MAX_ARRAY_SIZE 即為 `Integer.MAX_VALUE - 8`。
      */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

補充:System.arrayCopy()和Arrays.copyOf()方法

聯系: 看兩者源代碼可以發現 copyOf() 內部實際調用了 System.arraycopy() 方法

區別: arraycopy() 需要目標數組,將原數組拷貝到你自己定義的數組里或者原數組,而且可以選擇拷貝的起點和長度以及放入新數組中的位置 copyOf() 是系統自動在內部新建一個數組,並返回該數組。

參考原文:

https://www.cnblogs.com/baichunyu/p/12965241.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM