ArrayList內部實現原理


數組在創建的時候長度是固定的,那么就有往ArrayList中不斷添加對象的時候,那么ArrayList是如何管理這些數組的?

ArrayList內部通過Object[]實現,我們通過分析ArrayList的構造和add和remove和clear方法來分析

一、構造函數
1)空參構造

/**      
* Constructs a new {@code ArrayList} instance with zero initial capacity.      
*/     
public ArrayList() {         
    array = EmptyArray.OBJECT; 
}

array是一個Object[]類型。當我們new一個空參構造時系統調用了EmptyArray.OBJECT屬性,EmptyArray僅
僅是一個系統的類庫,該類源碼如下:

 

public final class EmptyArray {
     private EmptyArray() {}

     public static final boolean[] BOOLEAN = new boolean[0];
     public static final byte[] BYTE = new byte[0];
     public static final char[] CHAR = new char[0];
     public static final double[] DOUBLE = new double[0];
     public static final int[] INT = new int[0];
     public static final Class<?>[] CLASS = new Class[0];
     public static final Object[] OBJECT = new Object[0];
     public static final String[] STRING = new String[0];
     public static final Throwable[] THROWABLE = new Throwable[0];
     public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0]; }

也就是說當我們new 一個空參ArrayList的時候,系統內部使用了一個new Object[0]數組。 

2)帶參構造1 

 /** 
     * Constructs a new instance of {@code ArrayList} with the specified 
 * initial capacity.
*      
* @param capacity      
*            the initial capacity of this {@code ArrayList}.      
*/
     public ArrayList(int capacity) {
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity); 
        } 
        array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]); 
}

該構造函數傳入一個int值,該值作為數組的長度值。如果該值小於0,則拋出一個運行時異常。如果等於0,則
使用一個空數組,如果大於0,則創建一個長度為該值的新數組。

3)帶參構造2 

/**
      * Constructs a new instance of {@code ArrayList} containing the elements of 
      * the specified collection. 
      *
      * @param collection
      *            the collection of elements to add.
      */
     public ArrayList(Collection<? extends E> collection) {
         if (collection == null) {
             throw new NullPointerException("collection == null");
         } 
 
        Object[] a = collection.toArray();
         if (a.getClass() != Object[].class) {
             Object[] newArray = new Object[a.length];
             System.arraycopy(a, 0, newArray, 0, a.length);
             a = newArray;
         } 
        array = a;
         size = a.length;
     }  

若調用構造函數的時候傳入了一個Collection的子類,那么先判斷集合是否為null,為null則拋出空指針異常.如果不是則將集合轉換為數組a,然后將該數組賦值為成員變量array,將該數組的長度作為成員變量size.這里面它先判斷a.getClass是否等於Object[].class,一般都是相等的,判斷是為了增加安全性,toArray方法是Collection接口定義的,因此其所有的子類都是這樣的方法,list集合的toArray和Set集合的toArray返回的都是Object[]數組.

二、add方法
add方法有兩個重載,這里只研究最簡單的那個. 

  /** 
     * Adds the specified object at the end of this {@code ArrayList}. 
     *
     * @param object
      * 
           the object to add. 
     * @return always true 
     */
     @Override public boolean add(E object) {
         Object[] a = array;
         int s = size;
         if (s == a.length) {
             Object[] newArray = new Object[s +
                     (s < (MIN_CAPACITY_INCREMENT / 2) ? 
                     MIN_CAPACITY_INCREMENT : s >> 1)];
             System.arraycopy(a, 0, newArray, 0, s);
             array = a = newArray;
         }
         a[s] = object;
         size = s + 1;
         modCount++;
         return true;
     }

1、首先將成員變量array賦值給局部變量a,將成員變量size賦值給局部變量s。
2、判斷集合的長度s是否等於數組的長度(如果集合的長度已經等於數組的長度了,說明數組已經滿了,該重新
分配新數組了),重新分配數組的時候需要計算新分配內存的空間大小,如果當前的長度小於
MIN_CAPACITY_INCREMENT/2(這個常量值是12,除以2就是 6,也就是如果當前集合長度小於6)則分配12個
長度,如果集合長度大於6則分配當前長度s的一半長度。這里面用到了三元運算符和位運算,s >> 1,意思就是將
s往右移1位,相當於s=s/2,只不過位運算是效率最高的運算。
3、將新添加的object對象作為數組的a[s]個元素。
4、修改集合長度size為s+1
5、modCotun++,該變量是父類中聲明的,用於記錄集合修改的次數,記錄集合修改的次數是為了防止在用迭代
器迭代集合時避免並發修改異常,或者說用於判斷是否出現並發修改異常的。
6、return true,這個返回值意義不大,因為一直返回true,除非報了一個運行時異常。

三、remove方法
remove方法有兩個重載,這里只研究remove(int index)方法。

 

  /** 
      * Removes the object at the specified location from this list.
      *
      * @param index
      *            the index of the object to remove.
      * @return the removed object.
      * @throws IndexOutOfBoundsException
      *             when {@code location < 0 || location >= size()}
      */
     @Override public E remove(int index) {
         Object[] a = array;
         int s = size;
         if (index >= s) {
             throwIndexOutOfBoundsException(index, s);
         }
         @SuppressWarnings("unchecked")  
        E result = (E) a[index];
         System.arraycopy(a, index + 1, a, index, --s - index);
         a[s] = null;  // Prevent memory leak
         size = s;
         modCount++;
         return result;
     }

1、先將成員變量array和size賦值給局部變量a和s。
2、判斷形參index是否大於等於集合的長度,如果成了則拋出運行時異常
3、獲取數組中腳標為index的對象result,該對象作為方法的返回值
4、調用System的arraycopy函數

5、接下來就是很重要的一個工作,因為刪除了一個元素,而且集合整體向前移動了一位,因此需要將集合最后一個元素設置為null,否則就可能內存泄露。 

6、重新給成員變量array和size賦值
7、記錄修改次數
8、返回刪除的元素

四、clear方法 

  /**
      * Removes all elements from this {@code ArrayList}, leaving it empty. 
     *
      * @see #isEmpty 
     * @see #size
      */
     @Override public void clear() { 
        if (size != 0) { 
            Arrays.fill(array, 0, size, null);
             size = 0;
             modCount++; 
        }
     } 

如果集合長度不等於0,則將所有數組的值都設置為null,然后將成員變量size 設置為0即可,最后讓修改記錄加1.

  

  

 


免責聲明!

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



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