天天看點

ArrayList 源碼閱讀記錄ArrayList

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/weixin_40254498/article/details/81383693

ArrayList

ArrayList是基于

數組

實作的,是一個

動态數組

,其容量能自動增長,類似于C語言中的動态申請記憶體,動态增長記憶體。

  • 不是線程安全

    的,隻能用在

    單線程環境

  • 多線程環境下可以考慮用

    Collections.synchronizedList(List l)函數

    傳回一個線程安全的ArrayList類
  • 也可以使用concurrent并發包下的

    CopyOnWriteArrayList類

  • 實作了Serializable接口

    ,是以它支援序列化,能夠通過序列化傳輸
  • 實作了

    RandomAccess接口

    ,支援快速随機通路,實際上就是通過下标序号進行快速通路
  • Cloneable接口

    ,能被克隆

根據JDK版本的不同 ,構造方法也不同

/**
*   JDK 1.8
*/
/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;
/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

 /**
  * 上面這個對象數組就是其存儲元素的資料結構,前面有一個java關鍵字transient
  * 這個關鍵字是去序列化的意思,即,在這個類序列化後儲存到磁盤或者輸出到輸出流的時候
  * 這個對象數組是不被儲存或者輸出的。(這個不是下面的翻譯,對transient解釋)
  * 
  * 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

/**
 * ArrayList帶容量大小的構造函數。  
 * 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
 */
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);
   }
}

/**
 * 無參構造方法構造的ArrayList的預設傳回空數組
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
   this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 帶有Collection參數的構造方法
 * 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
 */
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;
   }
}


/**
* JDK 1.7/1.6
*/
/** 
* 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 
  */  
 public ArrayList(int initialCapacity) {  
     super();  
     if (initialCapacity < 0)  
         throw new IllegalArgumentException("Illegal Capacity: "+  
                                            initialCapacity);  
     this.elementData = new Object[initialCapacity];  
 }  

 /** 
  * 無參構造直接傳回了this(10);預設10 這也是與1.8不同的地方
  * Constructs an empty list with an initial capacity of ten. 
  */  
 public ArrayList() {  
     this(10);  
 }  

 /** 
  * Constructs a list containing the elements of the specified 
  * collection, in the order they are returned by the collection's 
  * iterator. 
  * 在這1.8也多了一個判斷
  * @param c the collection whose elements are to be placed into this list 
  * @throws NullPointerException if the specified collection is null 
  */  
 public ArrayList(Collection<? extends E> c) {  
     elementData = c.toArray();  
     size = elementData.length;  
     // c.toArray might (incorrectly) not return Object[] (see 6260652)  
     if (elementData.getClass() != Object[].class)  
         elementData = Arrays.copyOf(elementData, size, Object[].class);  
 }  
           

為什麼用到 transient?

這就跟這個ArrayList的特性有關,我們知道ArrayList的容量,也就是這個數組的容量,一般都是預留一些容量,等到容量不夠時再拓展,那麼就會出現容量還有備援的情況,如果這時候進行序列化,整個數組都會被序列化,連後面沒有意義空元素的也被序列化。這些是不應該被存儲的。是以java的設計者,就為這個類提供了一個writeObject方法,在實作了Serializable接口的類,如果這個類提供了writeObject方法,那麼在進行序列化的時候就會通過writeObject方法進行序列化,是以ArrayList的writeObject方法就會顯式的為每個實際的數組元素進行序列化,隻序列化有用的元素。

為什麼源碼中大量地調用了Arrays.copyof()和System.arraycopy()方法?

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    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;
}           

最後都調用了System.arraycopy()方法。

/**
* 該方法被标記了native,調用了系統的C/C++代碼,在JDK中是看不到的,
* 但在openJDK中可以看到其源碼。該函數實際上最終調用了C語言的memmove()函數,
* 是以它可以保證同一個數組内元素的正确複制 和移動,比一般的複制方法的實作效率要高很多,
* 很适合用來批量處理數組。Java強烈推薦在複制大量數組元素時用該方法,以取得更高的效率。
* 這也說明了ArrayList 與數組
*/  
public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);           

JDK1.6 開始擴容辦法也不一樣

/** 
 * JDK1.6
 * Increases the capacity of this <tt>ArrayList</tt> instance, if 
 * necessary, to ensure that it can hold at least the number of elements 
 * specified by the minimum capacity argument. 
 * 
 * @param   minCapacity   the desired minimum capacity 
 */  
 public void ensureCapacity(int minCapacity) {  
     modCount++;  
     int oldCapacity = elementData.length;  
     if (minCapacity > oldCapacity) {  
         Object oldData[] = elementData;  
         int newCapacity = (oldCapacity * 3)/2 + 1;  
         if (newCapacity < minCapacity)  
             newCapacity = minCapacity;  
         // minCapacity is usually close to size, so this is a win:  
         elementData = Arrays.copyOf(elementData, newCapacity);  
     }  
 } 
 /**
  * JDK1.8 
  */
 /**
  * The maximum size of array to allocate.
  * Some VMs reserve some header words in an array.
  * Attempts to allocate larger arrays may result in
  * OutOfMemoryError: Requested array size exceeds VM limit
  */
  private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 /**
  * Increases the capacity to ensure that it can hold at least the
  * number of elements specified by the minimum capacity argument.
  *
  * @param minCapacity the desired minimum capacity
  */
  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);
  }
   private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
   }           
  • 1.7JDK開始使用的是位運算
  • 在算出newCapacity時,其沒有和ArrayList所定義的MAX_ARRAY_SIZE作比較,為什麼沒有進行比較呢,原因是jdk1.6沒有定義這個MAX_ARRAY_SIZE最大容量,也就是說,其沒有最大容量限制的,但是jdk1.7以上做了一個改進,進行了容量限制。

Java 集合中常見 checkForComodification()方法的作用? modCount和expectedModCount作用?

ArrayList 快速通路

ArrayList基于數組實作,可以通過下标索引直接查找到指定位置的元素,是以查找效率高,但每次插入或删除元素,就要大量地移動元素,插入删除元素的效率低。

/**
* 先檢查索引 然後校驗操作正确
*/
public E get(int var1) {
   this.rangeCheck(var1);
    return this.elementData(var1);
}