[Java集合源碼] 一步一步解析 ArrayList 中常用方法的源碼

1. 默認數組大小爲10,在構造函數不傳入參數的時候使用默認大小

// ArrayList繼承了AbstractList,實現了RandomAccess、Cloneable和java.io.Serializable
 public class ArrayList<E> extends AbstractList<E>
         implements List<E>, RandomAccess, Cloneable, java.io.Serializable
 {
 ​
     // 默認數組大小
     private static final int DEFAULT_CAPACITY = 10;
 ​
     // 默認的空數組,初始化時不傳入初始化大小默認是一個空數組
     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 ​
     // ArrayList的底層結構,本質上是一個數組
     transient Object[] elementData;
 ​
     // 初始化時不傳入初始化大小默認是一個空數組,默認大小爲10
     public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     }
 }

2. 可以使用另外一個集合,在構建ArrayList時作爲參數傳遞到構造函數中,來初始化ArrayList:

    public ArrayList(Collection<? extends E> c) {
         elementData = c.toArray();
         // 集合c大小不爲0
         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;
         }
     }

3. ensureCapacity 判斷是否需要擴容,以保證數組能hold住minCapacity個元素     

    public void ensureCapacity(int minCapacity) {
         int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
             // any size if not default element table 如果數組不是默認的數組(即{}),則爲0
             ? 0
             // larger than default for default empty table. It's already
             // supposed to be at default size.
             // 如果數組等於默認數組,則爲DEFAULT_CAPACITY(值爲10)
             : DEFAULT_CAPACITY;
 ​
        // 判斷minCapacity是否大於需要擴容的最小值
         if (minCapacity > minExpand) {
             ensureExplicitCapacity(minCapacity);
         }
     }
 ​
     private void ensureExplicitCapacity(int minCapacity) {
         // the number of times this list has been structurally modified,
         // Structural modifications are those that change the size of the list
         modCount++;
 ​
         // 如果設置的最小容量比真實的數組大小大,則調用grow擴容
         if (minCapacity - elementData.length > 0)
             grow(minCapacity);
     }
 ​
     /**
      * Increases the capacity to ensure that it can hold at least the
      * number of elements specified by the minimum capacity argument.
      * 擴容以保證數組能夠放下minCapacity個元素
      *
      * @param minCapacity the desired minimum capacity
      */
     private void grow(int minCapacity) {
         // 原數組大小
         int oldCapacity = elementData.length;
         // 新數組大小,爲原大小的1.5倍
         int newCapacity = oldCapacity + (oldCapacity >> 1);
         // 如果新數組的大小比設定的minCapacity小,則新數組大小設爲minCapacity
         if (newCapacity - minCapacity < 0)
             newCapacity = minCapacity;
         // 如果新數組的大小比默認最大數組大小(Integer.MAX_VALUE - 8)大,則調用hugeCapacity方法
         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);
     }
     
     // 當newCapacity大於默認最大數組大小時調用
     private static int hugeCapacity(int minCapacity) {
         if (minCapacity < 0) // overflow
             throw new OutOfMemoryError();
         // 如果要設定的最小容量比默認數組最大容量大,則新數組的大小設爲整數最大值,否則新數組的
         // 大小設爲默認數組最大容量(Integer.MAX_VALUE - 8)
         return (minCapacity > MAX_ARRAY_SIZE) ?
             Integer.MAX_VALUE :
             MAX_ARRAY_SIZE;
     }

4. 兩個簡單的獲取屬性的方法,直接通過屬性值得到 ​      

    public int size() {
         return size;
     }
 ​
     public boolean isEmpty() {
         return size == 0;
     }

5. contains,indexOf,lastIndexOf方法 

    public boolean contains(Object o) {
         return indexOf(o) >= 0;
     }
 ​
     public int indexOf(Object o) {
         // 如果要查的元素爲null,則遍歷數組找出元素爲null的下標,
         if (o == null) {
             for (int i = 0; i < size; i++)
                 if (elementData[i]==null)
                     return i;
         // 否則同樣遍歷數組找出相同的元素的下標
         } else {
             for (int i = 0; i < size; i++)
                 if (o.equals(elementData[i]))
                     return i;
         }
         
         // 如果找不到直接返回-1
         return -1;
     }
 ​
     // 查找邏輯與indexOf相同,只不過遍歷是從後向前開始
     public int lastIndexOf(Object o) {
         if (o == null) {
             for (int i = size-1; i >= 0; i--)
                 if (elementData[i]==null)
                     return i;
         } else {
             for (int i = size-1; i >= 0; i--)
                 if (o.equals(elementData[i]))
                     return i;
         }
         return -1;
     }
6. clone方法,實際是返回了一個ArrayList實例的淺複製版本
     public Object clone() {
         try {
             // 調用Object類中的clone方法,此方法爲natice方法
             ArrayList<?> v = (ArrayList<?>) super.clone();
             // 將數組及modCount賦值到v中
             v.elementData = Arrays.copyOf(elementData, size);
             v.modCount = 0;
             return v;
         } catch (CloneNotSupportedException e) {
             // this shouldn't happen, since we are Cloneable
             throw new InternalError(e);
         }
     }
7. get,set方法
    public E get(int index) {
  			// 對index進行範圍驗證,檢查index是否超出數組的下標範圍
        rangeCheck(index);

        return elementData(index);
    }

		public E set(int index, E element) {
      	// 同樣對index進行範圍驗證,檢查index是否超出數組的下標範圍
        rangeCheck(index);
				
      	// 取出index下標位置的舊值
        E oldValue = elementData(index);
      	// 將新值設置進index位置
        elementData[index] = element;
      	// 返回舊值
        return oldValue;
    }

		private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
8. 兩個add方法
// 第一個add方法,直接添加元素,一般加在數組最後
     public boolean add(E e) {
         // 添加一個元素的操作,實際上數組要保證容量不少於當前size加1,如果不夠則要擴容
         ensureCapacityInternal(size + 1);  // Increments modCount!!
         // 容量得到保證後,插入新值,並返回添加成功(true)
         elementData[size++] = e;
         return true;
     }
 ​
     private void ensureCapacityInternal(int minCapacity) {
         // 內部調用了ensureExplicitCapacity方法,見下面
         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
     }
     
     // 上面已經介紹過,如果數組容量不夠則要擴容
     private void ensureExplicitCapacity(int minCapacity) {
         modCount++;
 ​
         // overflow-conscious code
         if (minCapacity - elementData.length > 0)
             grow(minCapacity);
     }
 ​
 ​
     // 第二個add方法,指定新元素要插入的下標
     public void add(int index, E element) {
         // 保證index在數組有效範圍內
         rangeCheckForAdd(index);
 ​
         // 添加一個元素的操作,實際上數組要保證容量不少於當前size加1,如果不夠則要擴容
         ensureCapacityInternal(size + 1);  // Increments modCount!!
       
         // 調用系統層級的native複製方法,將原數組index位置及之後的所有元素,複製到數組的index + 1
         // 位置,需要執行復制的數組元素數量是size - index
         System.arraycopy(elementData, index, elementData, index + 1,
                          size - index);
         // 複製完成後,纔開始將新值插入,保證不會覆蓋到原有的index位置的元素
         elementData[index] = element;
         // 容量+1s
         size++;
     }
 ​
     private void rangeCheckForAdd(int index) {
         if (index > size || index < 0)
             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
     }
9. remove方法
     // 通過下標刪除元素
     public E remove(int index) {
         // 對index進行範圍驗證,檢查index是否超出數組的下標範圍
         rangeCheck(index);
 ​
         modCount++;
         E oldValue = elementData(index);
 ​
         // 刪除元素時需要移動的元素個數
         int numMoved = size - index - 1;
         if (numMoved > 0)
             // 調用系統層級的native複製方法,將原數組index + 1位置及之後的所有元素,複製到數組的index位置,需要執行復制的數組元素數量是size - index - 1
             System.arraycopy(elementData, index+1, elementData, index,
                              numMoved);
         // 由於刪除了中間的元素,並且將之後的元素執行前移複製操作,數組最後一個位置置爲空
         elementData[--size] = null; // clear to let GC do its work
 ​
         return oldValue;
     }
 ​
   
     // 通過給定元素刪除數組中對應的元素,同樣需要遍歷數組判斷待刪除元素的下標
     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;
     }
     
     // 此處邏輯與上面的remove的部分邏輯差不多,將index + 1及之後的元素移動到index位置,最後一個位置智空
     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
     }
10. clear方法
    public void clear() {
         modCount++;
 ​
         // 遍歷所有位置將所有位置置空,讓後面GC可以進行垃圾回收
         for (int i = 0; i < size; i++)
             elementData[i] = null;
             
         // size置0
         size = 0;
     }
11. addAll方法
     // 集合c添加到原數組的後面
     public boolean addAll(Collection<? extends E> c) {
         // 將集合c轉爲數組
         Object[] a = c.toArray();
         // 要添加的數組的長度
         int numNew = a.length;
         // 數組要保證容量不少於當前數組的size+要添加進入的數組的長度numNew,如果不夠則要擴容
         ensureCapacityInternal(size + numNew);  // Increments modCount
         // 系統層級的native方法,將數組a複製到原數組的後面
         // a:源數組,0:源數組的開始位置,elementData:目標數組,size:目標數組的開始位置,numNew:需要進行復制的數組元素的長度
         System.arraycopy(a, 0, elementData, size, numNew);
         size += numNew;
         return numNew != 0;
     }
 ​
     // 集合c添加到源數組指定的index位置
     public boolean addAll(int index, Collection<? extends E> c) {
         // 保證index在數組有效範圍內
         rangeCheckForAdd(index);
 ​
         Object[] a = c.toArray();
         int numNew = a.length;
         ensureCapacityInternal(size + numNew);  // Increments modCount
 ​
         int numMoved = size - index;
         if (numMoved > 0)
             System.arraycopy(elementData, index, elementData, index + numNew,
                              numMoved);
 ​
         System.arraycopy(a, 0, elementData, index, numNew);
         size += numNew;
         return numNew != 0;
     }
12. removeRange方法
    // 移除某個範圍內的元素
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
      	// 移除元素範圍爲[fromIndex, toIndex],因此要向前移動的元素個數爲size - toIndex
        int numMoved = size - toIndex;
      	// 將toIndex位置之後的元素向前移動到toIndex處,完成remove操作
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // 將後面沒有用的位置置空,方便GC垃圾回收
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }
13. removeAll方法
    public boolean removeAll(Collection<?> c) {
         // 調用了Object.java裏面的方法,判斷集合是否爲空,空的話判處空指針異常
         Objects.requireNonNull(c);
         return batchRemove(c, false);
     }
     
     // Object.java裏面的方法
     public static <T> T requireNonNull(T obj) {
         if (obj == null)
             throw new NullPointerException();
         return obj;
     }
 ​
     private boolean batchRemove(Collection<?> c, boolean complement) {
         final Object[] elementData = this.elementData;
         int r = 0, w = 0;
         boolean modified = false;
         try {
             for (; r < size; r++)
                 if (c.contains(elementData[r]) == complement)
                     elementData[w++] = elementData[r];
         } finally {
             // c.contains()方法報異常,將剩下的元素賦值給elementData
             if (r != size) {
                 System.arraycopy(elementData, r,
                                  elementData, w,
                                  size - r);
                 w += size - r;
             }
             // 1. 如果w爲0,實際上集合c的元素與elementData的元素完全相同,效果類似clear,全爲null
             // 2. 如果w不爲0且小於size,表示elementData含有部分/全部集合c的元素,因爲上面try中
             // 的for循環已經將非相交元素前移,所以只需要將w位置之後的元素置空即可
             if (w != size) {
                 // clear to let GC do its work
                 for (int i = w; i < size; i++)
                     elementData[i] = null;
                 // 修改的次數
                 modCount += size - w;
                 // 最後elementData的大小
                 size = w;
                 // 表示完成了修改,返回true
                 modified = true;
             }
         }
         return modified;
     }

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