Java容器類源碼-ArrayList的最全的源碼分析

寫在前面

本文是針對Java 1.8的源代碼進行解析的,可能會和其餘版本有所出入。java

1、繼承和實現

  • **繼承:**AbstractList數組

  • **實現:**List<E>, RandomAccess, Cloneable, Serializable接口緩存

源代碼

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}

2、全局變量

1. 默認容量

private static final int DEFAULT_CAPACITY = 10;

2. 空的對象數組

private static final Object[] EMPTY_ELEMENTDATA = {};

3.默認的空數組

// 無參構造函數建立的數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

4.存放數據的數組的緩存變量,不可序列化

transient Object[] elementData;

5.數組的大小

private int size;

3、構造方法

1.帶有容量initialCapacity的構造方法

源碼解釋:多線程

public ArrayList(int initialCapacity) {
     // 若是初始化時ArrayList大小大於0
    if (initialCapacity > 0) {
          // new一個該大小的object數組賦給elementData
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) { // 若是大小爲0
          // 將空數組賦給elementData
        this.elementData = EMPTY_ELEMENTDATA;
    } else { // 小於0
          // 則拋出IllegalArgumentException異常
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

2.不帶參數的構造方法

源碼解釋:dom

public ArrayList() {
     // 直接將空數組賦給elementData  
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3.帶參數Collection的構造方法

源碼解釋:函數

參數c爲一個Collection,Collection的實現類大概有如下幾種經常使用類型:測試

  • List:元素能夠重複的容器
  • Set: 元素不可重複的容器
  • Queue:結構是一個隊列,先進先出

這個構造方法的意思是,將一個Collection實現類的對象轉換爲一個ArrayList,可是c容器裝的內容ui

必須爲ArrayList裝的內容的子類。例如,將一個裝了String內容的HashSet轉換爲裝了String內容的this

ArrayList,使得ArrayList的大小和值數組都是HashSet的大小和值數組。具體實現以下代碼,首先調.net

用c(Collection的具體實現類)的toArray方法,具體你們能夠看各個實現類的toArray方法,可是大

概意思都是將c容器轉換爲object類型的數組,由於它們的返回值都是object[]。之於下面的兩個判斷

是當獲得的elementData的類名不是Object類名的時候或者是長度爲0的時候纔會執行。

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

4、方法

1.trimToSize()

**說明:**將ArrayList的容量設置爲當前size的大小。首先須要明確一個概念,ArrayList的size就是ArrayList的元素個數,length是ArrayList申請的內容空間長度。ArrayList每次都會預申請多一點空間,以便添加元素的時候不須要每次都進行擴容操做,例如咱們的元素個數是10個,它申請的內存空間一定會大於10,即length>size,而這個方法就是把ArrayList的內存空間設置爲size,去除沒有用到的null值空間。這也就是咱們爲何每次在獲取數據長度是都是調用list.size()而不是list.length()。

**源碼解釋:**首先modCount是從類 java.util.AbstractList 繼承的字段,這個字段主要是爲了防止在多線程操做的狀況下,List發生結構性的變化,什麼意思呢?就是防止一個線程正在迭代,另一個線程進行對List進行remove操做,這樣當咱們迭代到最後一個元素時,很明顯此時List的最後一個元素爲空,那麼這時modCount就會告訴迭代器,讓其拋出異常 ConcurrentModificationException。

若是沒有這一個變量,那麼系統確定會報異常ArrayIndexOutOfBoundsException,這樣的異常顯然不是應該出現的(這些運行時錯誤都是使用者的邏輯錯誤致使的,咱們的JDK那麼高端,不會出現使用錯誤,咱們只拋出使用者形成的錯誤,而這個錯誤是設計者應該考慮的),爲了不出現這樣的異常,定義了檢查。

(引用自:郭無意,詳情能夠看他在知乎的回答:https://www.zhihu.com/question/24086463/answer/64717159)。

public void trimToSize() {
    modCount++;
     // 若是size小於length
    if (size < elementData.length) {
         // 從新將elementData設置大小爲size
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

2.size()

**說明:**返回ArrayList的大小

**源碼解釋:**直接返回size

public int size() {
    return size;
}

3.isEmpty()

**說明:**返回是否爲空

**源碼解釋: **直接返回判斷size==0

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

4.indexOf(Object o)

**說明:**對象o在ArrayList中的下標位置,若是存在返回位置i,不存在返回-1

**源碼解釋:**遍歷ArrayList的大小,比較o和容器內的元素,若相等,則返回位置i,若遍歷完都不相等,返回-1

public int indexOf(Object o) {
    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;
    }
    return -1;
}

5.contains(Object o)

**說明:**是否包含對象o

**源碼解釋:**調用indexOf()方法獲得下標,存在則下標>=0,不存在爲-1,即只要比較下標和0的大小便可。

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

6.lastIndexOf(Object o)

**說明:**返回容器內出現o的最後一個位置

**源碼解釋:**從後向前遍歷,獲得第一個出現對象o的位置,不存在則返回-1

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;
}

7.clone()

**說明:**返回此 ArrayList 實例的淺表副本。

源碼解釋:

public Object clone() {
    try {
         // 調用父類(翻看源碼可見是Object類)的clone方法獲得一個ArrayList副本
        ArrayList<?> v = (ArrayList<?>) super.clone();
         // 調用Arrays類的copyOf,將ArrayList的elementData數組賦值給副本的elementData數組
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
         // 返回副本v
        return v;
    } catch (CloneNotSupportedException e) {
        throw new InternalError(e);
    }
 }

8.toArray()

**說明:**ArrayList 實例轉換爲。

**源碼解釋:**直接調用Arrays類的copyOf。

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

9.toArray(T[] a)

**說明:**將ArrayList裏面的元素賦值到一個數組中去

**源碼解釋:**若是a的長度小於ArrayList的長度,直接調用Arrays類的copyOf,返回一個比a數組長度要大的新數組,裏面元素就是ArrayList裏面的元素;若是a的長度比ArrayList的長度大,那麼就調用System.arraycopy,將ArrayList的elementData數組賦值到a數組,而後把a數組的size位置賦值爲空。 public <T> T[] toArray(T[] a) { if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }

10.rangeCheck(int index)

**說明:**測試index是否越界

源碼解釋:

private void rangeCheck(int index) {
     // 若是下標超過ArrayList的數組長度
    if (index >= size)
         // 拋出IndexOutOfBoundsException異常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

11.get(int index)

**說明:**獲取index位置的元素

**源碼解釋:**先檢查是否越界,而後返回ArrayList的elementData數組index位置的元素。

public E get(int index) {
     // 檢查是否越界
    rangeCheck(index);
     // 返回ArrayList的elementData數組index位置的元素
    return elementData(index);
}

12.set(int index, E element)

**說明:**設置index位置的元素值了element,返回該位置的以前的值

源碼解釋:

public E set(int index, E element) {
     // 檢查是否越界  
    rangeCheck(index);
     // 調用elementData(index)獲取到當前位置的值
    E oldValue = elementData(index);
     // 將element賦值到ArrayList的elementData數組的第index位置
    elementData[index] = element;
    return oldValue;
}

13.ensureCapacityInternal(int minCapacity)

**說明:**獲得最小擴容量

源碼解釋:

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         // 獲取默認的容量和傳入參數的較大值
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

14.ensureExplicitCapacity(int minCapacity)

**說明:**判斷是否須要擴容

源碼解釋:

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 若是最小須要空間比elementData的內存空間要大,則須要擴容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

15.grow()方法

**說明:**幫助ArrayList動態擴容的核心方法

源碼解釋:

// MAX_VALUE爲231-1,MAX_ARRAY_SIZE 就是獲取Java中int的最大限制,以防止越界  
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
    // 獲取到ArrayList中elementData數組的內存空間長度
    int oldCapacity = elementData.length;
    // 擴容至原來的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 再判斷一下新數組的容量夠不夠,夠了就直接使用這個長度建立新數組, 
    // 不夠就將數組長度設置爲須要的長度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 判斷有沒超過最大限制
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 調用Arrays.copyOf方法將elementData數組指向新的內存空間時newCapacity的連續空間
    // 並將elementData的數據複製到新的內存空間
    elementData = Arrays.copyOf(elementData, newCapacity);
}

16.add(E e)

**說明:**添加元素e

源碼解釋:

public boolean add(E e) {
     // 擴容
    ensureCapacityInternal(size + 1);  
    // 將e賦值給elementData的size+1的位置。
    elementData[size++] = e;
    return true;
}

17.add(int index, E element)

**說明:**在ArrayList的index位置,添加元素element

源碼解釋:

public void add(int index, E element) {
    // 判斷index是否越界  
    rangeCheckForAdd(index);
     // 擴容
    ensureCapacityInternal(size + 1);  
     // 將elementData從index位置開始,複製到elementData的index+1開始的連續空間
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
     // 在elementData的index位置賦值element
    elementData[index] = element;
     // ArrayList的大小加一  
    size++;
}

18.remove(int index)

**說明:**在ArrayList的移除index位置的元素

源碼解釋:

public E remove(int index) {
     // 判斷是否越界  
    rangeCheck(index);
    modCount++;
     // 讀取舊值  
    E oldValue = elementData(index);
     // 獲取index位置開始到最後一個位置的個數
    int numMoved = size - index - 1;
    if (numMoved > 0)
         // 將elementData數組index+1位置開始拷貝到elementData從index開始的空間
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
     // 使size-1 ,設置elementData的size位置爲空,讓GC來清理內存空間
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}

19.remove(Object o)

**說明:**在ArrayList的移除對象爲O的元素,跟indexOf方法思想基本一致

源碼解釋:

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;
}

20.clear()

**說明:**設置所有元素爲null值,並設置size爲0。

**源碼解釋:**可見clear操做並非從空間內刪除,只是設置爲null值,等待垃圾回收機制來回收而已,把size設置爲0,以便咱們不會瀏覽到null值的內存空間。

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

21.addAll(Collection<? extends E> c)

**說明:**將Collection c的所有元素添加到ArrayList中

源碼解釋:

public boolean addAll(Collection<? extends E> c) {
     // 將c轉換爲數組a
    Object[] a = c.toArray();
     // 獲取a佔的內存空間長度賦值給numNew
    int numNew = a.length;
     // 擴容至size + numNew
    ensureCapacityInternal(size + numNew);  // Increments modCount
     // 將a的第0位開始拷貝至elementData的size位開始,拷貝長度爲numNew
    System.arraycopy(a, 0, elementData, size, numNew);
     // 將size增長numNew  
    size += numNew;
     // 若是c爲空,返回false,c不爲空,返回true
    return numNew != 0;
}

22.addAll(int index, Collection<? extends E> c)

**說明:**從第index位開始,將c所有拷貝到ArrayList

源碼解釋:

public boolean addAll(int index, Collection<? extends E> c) {
     // 判斷index大於size或者是小於0,若是是,則拋出IndexOutOfBoundsException異常
    rangeCheckForAdd(index);
     // 將c轉換爲數組a
    Object[] a = c.toArray();
    int numNew = a.length;
     // 擴容至size + numNew
    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;
}

24.batchRemove(Collection<?> c, boolean complement)

**說明:**根據complement值,將ArrayList中包含c中元素的元素刪除或者保留

源碼解釋:

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
     // 定義一個w,一個r,兩個同時右移   
    int r = 0, w = 0;
    boolean modified = false;
    try {
         // r先右移
        for (; r < size; r++)
              // 若是c中不包含elementData[r]這個元素
            if (c.contains(elementData[r]) == complement)
                  // 則直接將r位置的元素賦值給w位置的元素,w自增
                elementData[w++] = elementData[r];
    } finally {
        // 防止拋出異常致使上面r的右移過程沒完成
        if (r != size) {
              // 將r未右移完成的位置的元素賦值給w右邊位置的元素
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
              // 修改w值增長size-r
            w += size - r;
        }
        if (w != size) {
            // 若是有被覆蓋掉的元素,則將w後面的元素都賦值爲null
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
              // 修改size爲w
            size = w;
            modified = true;
        }
    }
    return modified;
}

25.removeAll(Collection<?> c)

**說明:**ArrayList移除c中的全部元素

源碼解釋:

public boolean removeAll(Collection<?> c) {
     // 若是c爲空,則拋出空指針異常
    Objects.requireNonNull(c);
     // 調用batchRemove移除c中的元素
    return batchRemove(c, false);
}

26.retainAll(Collection<?> c)

**說明:**和removeAll相反,僅保留c中全部的元素

源碼解釋:

public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
     // 調用batchRemove保留c中的元素
    return batchRemove(c, true);
}

27.iterator()

**說明:**返回一個Iterator對象,Itr爲ArrayList的一個內部類,其實現了Iterator<E>接口

public Iterator<E> iterator() {
    return new Itr();
}

28.listIterator()

**說明:**返回一個ListIterator對象,ListItr爲ArrayList的一個內部類,其實現了ListIterator<E> 接口

源碼解釋:

public ListIterator<E> listIterator() {
    return new ListItr(0);
}

29.listIterator(int index)

說明:返回一個從index開始的ListIterator對象

源碼解釋:

public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}

30.subList(int fromIndex, int toIndex)

**說明:**根據兩個參數,獲取到一個子序列

源碼解釋:

public List<E> subList(int fromIndex, int toIndex) {
     // 檢查異常
    subListRangeCheck(fromIndex, toIndex, size);
     // 調用SubList類的構造方法
    return new SubList(this, 0, fromIndex, toIndex);
}

5、內部類

(1)private class Itr implements Iterator<E>
(2)private class ListItr extends Itr implements ListIterator<E>
(3)private class SubList extends AbstractList<E> implements RandomAccess
(4)static final class ArrayListSpliterator<E> implements Spliterator<E>

ArrayList有四個內部類,

其中的Itr是實現了Iterator接口,同時重寫了裏面的hasNext(),next(),remove()等方法;

其中的ListItr繼承Itr,實現了ListIterator接口,同時重寫了hasPrevious(),nextIndex(), previousIndex(),previous(),set(E e),add(E e)等方法,因此這也能夠看出了Iterator和ListIterator的區別,就是ListIterator在Iterator的基礎上增長了添加對象,修改對象,逆向遍歷等方法,這些是Iterator不能實現的。具體能夠參考http://blog.csdn.net/a597926661/article/details/7679765。

其中的SubList繼承AbstractList,實現了RandmAccess接口,類內部實現了對子序列的增刪改查等方法,但它同時也充分利用了內部類的優勢,就是共享ArrayList的全局變量,例如檢查器變量modCount,數組elementData等,因此SubList進行的增刪改查操做都是對ArrayList的數組進行的,並無建立新的數組。

最後一個比較我的比較少接觸,你們須要自行度娘。

End

筆者技術真的是通常般,寫這個爲了加深理解的同時給懼怕看源代碼的朋友一點鼓勵,因此筆者在寫的過程當中有查閱不少資料來努力減小錯誤,可是若有錯漏之處,但願大神們指出,我會第一時間修改,以避免誤人子弟,也但願和筆者同樣基礎不夠好的朋友不要畏懼看源碼,源碼看起來並不會很難,並且多看源代碼會對Java更深入的理解。

相關文章
相關標籤/搜索