ArrayList源碼詳解

ArrayList 底層源碼詳解

定義: 一個實現了List接口的可調整大小的數組,實現了List接口所有的可選操作並且可以容納所有類型的對象包括null,同時提供了方法來改變List容量的大小。

特徵:

  • 1 可自動擴容,和vector相比是非線程安全的。
  • 2 可以通過List list = Collections.synchronizedList(new ArrayList(…));生成線程安全的List對象
  • 3 ArrayList實現了Iterator接口可以通過iterator()方法獲取到迭代器
  • 4 由於ArrayList是非線程安全的,所以如果同時它進行遍歷和該變它的內部接口會throw ConcurrentModificationException。(可以通過iterator解決這個問題)

底層實現

實現結構圖

具體底層代碼實現

定義的一些常量
  /**
   * jdk1.8中ArrayList的默認容量大小爲10
   */
  private static final int DEFAULT_CAPACITY = 10;
  
  /**
   * 當ArrayList初始化爲0時默認賦值爲該空對象數組
   */
  private static final Object[] EMPTY_ELEMENTDATA = {};
  
   /**
   * 當ArrayList初始化時不指定大小時侯使用該對象數組進行初始化,同時用來區分初始化爲0的那種情況
   */
  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  
  /**
   * 存儲ArrayList元素的數組緩衝區。ArrayList元素的底層存儲都是基於該數組
   */
  transient Object[] elementData;

 /**
   * 用來記錄ArrayList中元素的個數
   */
  private int size;
常用方法的底層實現
  • 默認構造函數
   /**
    * 初始化一個容量爲10的空的ArrayList集合
    */
   public ArrayList(){
       this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
   }
  • 帶參構造函數
    /**
     * 參數initialCapacity代表ArrayList指定的初始化容量的大小
     * initialCapacity>0 初始化Object[] elementData數組大小爲指定的initialCapacity
     * initialCapacity=0 初始化Object[] elementData 爲空數組EMPTY_ELEMENTDATA
     * initialCapacity<0 拋出容量大小不合法的異常
     */
    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);
        }
    }


   /**
    * 參數 任意Collection對象
    * 1 將Collection對象轉換成數組
    * 2 如果轉換成的數組size==0 則創建一個容量爲0的ArrayList對象,否則將對象數組拷貝到elementData數組中去
    *        注意: toArray()方法返回的可能不是Object[]類型,例如Arrays.asList("asList")底層返回的就是一個String[]類型的數組,只是包裝在List<String>中。,所以這邊判斷了elementData.getClass()的類型,如果不是對象數組將它向上轉型成了對象數組。
    */
   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;
        }
    }
    
    
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
  • add(E e)方法 返回值爲boolean
    /**
     * E e代表可以添加的參數 如果實例化的時候指定了泛型類型則只能添加指定類型的元素,不然則不會進行類型檢查,e可以是任意類型的元素在編譯的時候會自動類型擦除當成Object來處理
     * 添加元素到集合中去
     *
     * ensureCapacityInternal(size+1) 
     * 添加一個元素的時候確保容量足夠大
     *
     * elementData[size++] = e;
     * 將元素添追加到elmentData數組的最後一位
     *
     * 返回boolean爲true
    */ 
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    
    /**
     * 確保添加的時候集合的容量足夠大
     *
     * 調用ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));方法進行擴容
     *
     * 調用calculateCapacity(elementData, minCapacity)計算集合需要達到的最小的容量
     */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    
    /**
     *  判斷elementData是否爲默認容量的數組,如果是則選取默認容量和添加元素後容量中的最大值
     *  如果不是直接返回minCapacity(需要達到的最小容量)
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    
    /**
     *  確保容量的具體方法
     *  
     *  modCount++; 用來記錄每一次結構性的調整 modCount繼承與abstractList
     *
     *  判斷添加元素後的容量是否大於當前集合的容量,如果超過了當前集合的容量就調用grow(minCapacity)進行擴容
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
     /**
      * 將容量擴展爲當前容量的1.5倍(利用位運算)
      * 
      * 如果擴大1.5後仍然比當前需要的容量小,就擴容至minCapcity大小
      * 如果擴大1.5後超過了MAX_ARRAY_SIZE 調用hugeCapacity(minCapacity);獲取指定容量
      * 
      * 將當前數組複製給新的擴容後的數組作爲新的集合
      */
     private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }
    
    
    /**
     *  如果需要容量大小0 拋出異常
     *  不然判斷是否大於MAX_ARRAY_SIZE 如果大於則返回Inter.MAX_VALUE 沒有則返回MAX_ARRAY_SIZE
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

    
  • add(int index, E element)方法(在指定位置插入元素,即將當前元素以及往後的元素向後移動,將元素插入該位置)
    /**
     * 1. 檢查索引位置是否合法 
     * 2. 確保容量足夠大(這部和add()方法判斷一樣)(這步 modCount +1 因爲改變了數據結構) ensureCapacityInternal(size + 1); 
     * 3. 將index後的內容向後移動一位System.arraycopy(elementData, index, elementData, index + 1,size - index);
     * 3. 將元素添加到index的位置 elementData[index] = element;
     * 4. size++; 元素的個數加一
     *
    public void add(int index, E element) {
        
         rangeCheckForAdd(index);
         // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * 判斷如果索引比元素個數大或者小於0 就拋出異常索引超過邊界的異常
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    
    注意有兩一種情況,如果index==size則表示在當前集合的最後添加一個元素,此時和add()方法效果一樣。
  • remove(int index) 將指定索引位置的元素刪除 返回值爲被刪除的元素
   /**
    *  1.檢查索引位置是否合法 rangeCheck(index);
    *  2. modCount++; 數據結構被修改記錄+1
    *  3. E oldValue = elementData(index);獲取索引位置的元素
    *  4. 計算需要向前移動的元素的個數然後將元素向前拷貝
    *  5. 將原本最後一位的元素釋放(size-1並且賦值爲null)
    *  6. 返回刪除的元素
    */
  
   public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }


    /**
     * 判讀index是否大於等於元素個數,滿足則拋出經常索引位置越界
     */
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    E elementData(int index) {
        return (E) elementData[index];
    }
  • remove(Object o) 將集合中指定元素刪除 返回值爲boolean
  /*
   * 1. 分兩種情況 如果o==null 則通過elementData[index] == null查找是否存爲null的元素
                     o!=null 則通過o.equals(elementData[index]查找是否存爲null的元素(考慮到對象和值對於值來說就是值是否相等,對象則需要是同一個對象地址纔會被刪除)
   */
   public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    
    
    /**
     * 1.modCount++ 修改了集合的數據結構
     * 2.計算需要向前移動的元素的數量
     * 3.拷貝元素
     * 4.將原本最後一位元素賦值爲null
     
     * 注意 此處刪除並沒有檢查索引是否合法 因爲只有元素存在纔會進行這一步索引保證了索引的合法性
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

  • set(int index, E element)(在容器的指定位置設置指定的值)
 /**
  * 1 檢查索引位置是否合法
  * 2 將該位置舊的值賦值給臨時的變量oldValue
  * 3 將新值存放到該索引位置
  * 4 返回被刪除的值
  */

 public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

  • get(int index)獲取指定索引位置的元素
   public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
  • clear() 清空集合中的所有元素
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章