Java源碼解讀-ArrayList

寫在前面

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

轉載自http://blog.csdn.net/ljcitworld/article/details/52041836

全局變量

1. 默認容量

[java] view plain copy
 print?
  1. private static final int DEFAULT_CAPACITY = 10;  

2. 空的對象數組

[java] view plain copy
 print?
  1. private static final Object[] EMPTY_ELEMENTDATA = {};  

3.默認的空數組

[java] view plain copy
 print?
  1. // 無參構造函數創建的數組  
  2. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  


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

[java] view plain copy
 print?
  1. transient Object[] elementData;  


5.數組的大小

[java] view plain copy
 print?
  1. private int size;  

構造方法

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

源碼解釋:
[java] view plain copy
 print?
  1. public ArrayList(int initialCapacity) {  
  2.      // 如果初始化時ArrayList大小大於0  
  3.     if (initialCapacity > 0) {  
  4.           // new一個該大小的object數組賦給elementData  
  5.         this.elementData = new Object[initialCapacity];  
  6.     } else if (initialCapacity == 0) { // 如果大小爲0  
  7.           // 將空數組賦給elementData  
  8.         this.elementData = EMPTY_ELEMENTDATA;  
  9.     } else { // 小於0  
  10.           // 則拋出IllegalArgumentException異常  
  11.         throw new IllegalArgumentException("Illegal Capacity: "+  
  12.                                            initialCapacity);  
  13.     }  
  14. }  

2.不帶參數的構造方法

源碼解釋:
[java] view plain copy
 print?
  1. public ArrayList() {  
  2.      // 直接將空數組賦給elementData    
  3.     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  
  4. }  

3.帶參數Collection的構造方法

源碼解釋:
參數c爲一個Collection,Collection的實現類大概有以下幾種常用類型:
  • List:元素可以重複的容器
  • Set: 元素不可重複的容器
  • Queue:結構是一個隊列,先進先出
這個構造方法的意思是,將一個Collection實現類的對象轉換爲一個ArrayList,但是c容器裝的內容必須爲ArrayList裝的內容的子類。例如,將一個裝了String內容的HashSet轉換爲裝了String內容的ArrayList,使得ArrayList的大小和值數組都是HashSet的大小和值數組。具體實現如下代碼,首先調用c(Collection的具體實現類)的toArray方法,具體大家可以看各個實現類的toArray方法,但是大概意思都是將c容器轉換爲object類型的數組,因爲它們的返回值都是object[]。之於下面的兩個判斷是當得到的elementData的類名不是Object類名的時候或者是長度爲0的時候纔會執行。
[java] view plain copy
 print?
  1. public ArrayList(Collection<? extends E> c) {  
  2.     elementData = c.toArray();  
  3.     if ((size = elementData.length) != 0) {  
  4.         if (elementData.getClass() != Object[].class)  
  5.             elementData = Arrays.copyOf(elementData, size, Object[].class);  
  6.     } else {  
  7.         // replace with empty array.  
  8.         this.elementData = EMPTY_ELEMENTDATA;  
  9.     }  
  10. }  

方法

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)。
[java] view plain copy
 print?
  1. public void trimToSize() {  
  2.     modCount++;  
  3.      // 如果size小於length  
  4.     if (size < elementData.length) {  
  5.          // 重新將elementData設置大小爲size  
  6.         elementData = (size == 0)  
  7.           ? EMPTY_ELEMENTDATA  
  8.           : Arrays.copyOf(elementData, size);  
  9.     }  
  10. }  

2.size()

說明:返回ArrayList的大小
源碼解釋:直接返回size
[java] view plain copy
 print?
  1. public int size() {  
  2.     return size;  
  3. }  

3.isEmpty()

說明:返回是否爲空
源碼解釋: 直接返回判斷size==0
[java] view plain copy
 print?
  1. public boolean isEmpty() {  
  2.     return size == 0;  
  3. }  

4.indexOf(Object o)

說明:對象o在ArrayList中的下標位置,如果存在返回位置i,不存在返回-1
源碼解釋:遍歷ArrayList的大小,比較o和容器內的元素,若相等,則返回位置i,若遍歷完都不相等,返回-1
[java] view plain copy
 print?
  1. public int indexOf(Object o) {  
  2.     if (o == null) {  
  3.         for (int i = 0; i < size; i++)  
  4.             if (elementData[i]==null)  
  5.                 return i;  
  6.     } else {  
  7.         for (int i = 0; i < size; i++)  
  8.             if (o.equals(elementData[i]))  
  9.                 return i;  
  10.     }  
  11.     return -1;  
  12. }  

5.contains(Object o)

說明:是否包含對象o
源碼解釋:調用indexOf()方法得到下標,存在則下標>=0,不存在爲-1,即只要比較下標和0的大小即可。
[java] view plain copy
 print?
  1. public boolean contains(Object o) {  
  2.     return indexOf(o) >= 0;  
  3. }  

6.lastIndexOf(Object o)

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

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

[java] view plain copy
 print?
  1. public int lastIndexOf(Object o) {  
  2.     if (o == null) {  
  3.         for (int i = size-1; i >= 0; i--)  
  4.             if (elementData[i]==null)  
  5.                 return i;  
  6.     } else {  
  7.         for (int i = size-1; i >= 0; i--)  
  8.             if (o.equals(elementData[i]))  
  9.                 return i;  
  10.     }  
  11.     return -1;  
  12. }  

7.clone()

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

源碼解釋:

[java] view plain copy
 print?
  1. public Object clone() {  
  2.     try {  
  3.          // 調用父類(翻看源碼可見是Object類)的clone方法得到一個ArrayList副本  
  4.         ArrayList<?> v = (ArrayList<?>) super.clone();  
  5.          // 調用Arrays類的copyOf,將ArrayList的elementData數組賦值給副本的elementData數組  
  6.         v.elementData = Arrays.copyOf(elementData, size);  
  7.         v.modCount = 0;  
  8.          // 返回副本v  
  9.         return v;  
  10.     } catch (CloneNotSupportedException e) {  
  11.         throw new InternalError(e);  
  12.     }  
  13.  }  

8.toArray()

說明:ArrayList 實例轉換爲。
源碼解釋:直接調用Arrays類的copyOf。
[java] view plain copy
 print?
  1. public Object[] toArray() {  
  2.     return Arrays.copyOf(elementData, size);  
  3. }  

9.toArray(T[] a)

說明:將ArrayList裏面的元素賦值到一個數組中去
源碼解釋:如果a的長度小於ArrayList的長度,直接調用Arrays類的copyOf,返回一個比a數組長度要大的新數組,裏面元素就是ArrayList裏面的元素;如果a的長度比ArrayList的長度大,那麼就調用System.arraycopy,將ArrayList的elementData數組賦值到a數組,然後把a數組的size位置賦值爲空。
[java] view plain copy
 print?
  1. public <T> T[] toArray(T[] a) {  
  2.     if (a.length < size)  
  3.         // Make a new array of a's runtime type, but my contents:  
  4.         return (T[]) Arrays.copyOf(elementData, size, a.getClass());  
  5.     System.arraycopy(elementData, 0, a, 0, size);  
  6.     if (a.length > size)  
  7.         a[size] = null;  
  8.     return a;  
  9.  }    

10.rangeCheck(int index)

說明:測試index是否越界
源碼解釋:

[java] view plain copy
 print?
  1. private void rangeCheck(int index) {  
  2.      // 如果下標超過ArrayList的數組長度  
  3.     if (index >= size)  
  4.          // 拋出IndexOutOfBoundsException異常  
  5.         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));  
  6. }  

11.get(int index)

說明:獲取index位置的元素
源碼解釋:先檢查是否越界,然後返回ArrayList的elementData數組index位置的元素。
[java] view plain copy
 print?
  1. public E get(int index) {  
  2.      // 檢查是否越界  
  3.     rangeCheck(index);  
  4.      // 返回ArrayList的elementData數組index位置的元素  
  5.     return elementData(index);  
  6. }  

12.set(int index, E element)

說明:設置index位置的元素值了element,返回該位置的之前的值
源碼解釋:
[java] view plain copy
 print?
  1. public E set(int index, E element) {  
  2.      // 檢查是否越界    
  3.     rangeCheck(index);  
  4.      // 調用elementData(index)獲取到當前位置的值  
  5.     E oldValue = elementData(index);  
  6.      // 將element賦值到ArrayList的elementData數組的第index位置  
  7.     elementData[index] = element;  
  8.     return oldValue;  
  9. }  

13.ensureCapacityInternal(int minCapacity)

說明:得到最小擴容量
源碼解釋:
[java] view plain copy
 print?
  1. private void ensureCapacityInternal(int minCapacity) {  
  2.     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  
  3.          // 獲取默認的容量和傳入參數的較大值  
  4.         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  
  5.     }  
  6.     ensureExplicitCapacity(minCapacity);  
  7. }  

14.ensureExplicitCapacity(int minCapacity)

說明:判斷是否需要擴容
源碼解釋:

[java] view plain copy
 print?
  1. private void ensureExplicitCapacity(int minCapacity) {  
  2.     modCount++;  
  3.     // 如果最小需要空間比elementData的內存空間要大,則需要擴容  
  4.     if (minCapacity - elementData.length > 0)  
  5.         grow(minCapacity);  
  6. }  

15.grow()方法

說明:幫助ArrayList動態擴容的核心方法
源碼解釋:
[java] view plain copy
 print?
  1. // MAX_VALUE爲231-1,MAX_ARRAY_SIZE 就是獲取Java中int的最大限制,以防止越界    
  2. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  
  3.   
  4.   
  5. private void grow(int minCapacity) {  
  6.     // 獲取到ArrayList中elementData數組的內存空間長度  
  7.     int oldCapacity = elementData.length;  
  8.     // 擴容至原來的1.5倍  
  9.     int newCapacity = oldCapacity + (oldCapacity >> 1);  
  10.     // 再判斷一下新數組的容量夠不夠,夠了就直接使用這個長度創建新數組,   
  11.     // 不夠就將數組長度設置爲需要的長度  
  12.     if (newCapacity - minCapacity < 0)  
  13.         newCapacity = minCapacity;  
  14.     // 判斷有沒超過最大限制  
  15.     if (newCapacity - MAX_ARRAY_SIZE > 0)  
  16.         newCapacity = hugeCapacity(minCapacity);  
  17.     // 調用Arrays.copyOf方法將elementData數組指向新的內存空間時newCapacity的連續空間  
  18.     // 並將elementData的數據複製到新的內存空間  
  19.     elementData = Arrays.copyOf(elementData, newCapacity);  
  20. }  

16.add(E e)

說明:添加元素e
源碼解釋:
[java] view plain copy
 print?
  1. public boolean add(E e) {  
  2.      // 擴容  
  3.     ensureCapacityInternal(size + 1);    
  4.     // 將e賦值給elementData的size+1的位置。  
  5.     elementData[size++] = e;  
  6.     return true;  
  7. }  

17.add(int index, E element)

說明:在ArrayList的index位置,添加元素element
源碼解釋:
[java] view plain copy
 print?
  1. public void add(int index, E element) {  
  2.     // 判斷index是否越界    
  3.     rangeCheckForAdd(index);  
  4.      // 擴容  
  5.     ensureCapacityInternal(size + 1);    
  6.      // 將elementData從index位置開始,複製到elementData的index+1開始的連續空間  
  7.     System.arraycopy(elementData, index, elementData, index + 1,  
  8.                      size - index);  
  9.      // 在elementData的index位置賦值element  
  10.     elementData[index] = element;  
  11.      // ArrayList的大小加一    
  12.     size++;  
  13. }  

18.remove(int index)

說明:在ArrayList的移除index位置的元素
源碼解釋:
[java] view plain copy
 print?
  1. public E remove(int index) {  
  2.      // 判斷是否越界    
  3.     rangeCheck(index);  
  4.     modCount++;  
  5.      // 讀取舊值    
  6.     E oldValue = elementData(index);  
  7.      // 獲取index位置開始到最後一個位置的個數  
  8.     int numMoved = size - index - 1;  
  9.     if (numMoved > 0)  
  10.          // 將elementData數組index+1位置開始拷貝到elementData從index開始的空間  
  11.         System.arraycopy(elementData, index+1, elementData, index,  
  12.                          numMoved);  
  13.      // 使size-1 ,設置elementData的size位置爲空,讓GC來清理內存空間  
  14.     elementData[--size] = null// clear to let GC do its work  
  15.     return oldValue;  
  16. }  

19.remove(Object o)

說明:在ArrayList的移除對象爲O的元素,跟indexOf方法思想基本一致
源碼解釋:
[java] view plain copy
 print?
  1. public boolean remove(Object o) {  
  2.     if (o == null) {  
  3.         for (int index = 0; index < size; index++)  
  4.             if (elementData[index] == null) {  
  5.                 fastRemove(index);  
  6.                 return true;  
  7.             }  
  8.     } else {  
  9.         for (int index = 0; index < size; index++)  
  10.             if (o.equals(elementData[index])) {  
  11.                 fastRemove(index);  
  12.                 return true;  
  13.             }  
  14.     }  
  15.     return false;  
  16. }  

20.clear()

說明:設置全部元素爲null值,並設置size爲0。
源碼解釋:可見clear操作並不是從空間內刪除,只是設置爲null值,等待垃圾回收機制來回收而已,把size設置爲0,以便我們不會瀏覽到null值的內存空間。
[java] view plain copy
 print?
  1. public void clear() {  
  2.     modCount++;  
  3.     // clear to let GC do its work  
  4.     for (int i = 0; i < size; i++)  
  5.         elementData[i] = null;  
  6.     size = 0;  
  7. }  

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

說明:將Collection c的全部元素添加到ArrayList中
源碼解釋:
[java] view plain copy
 print?
  1. public boolean addAll(Collection<? extends E> c) {  
  2.      // 將c轉換爲數組a  
  3.     Object[] a = c.toArray();  
  4.      // 獲取a佔的內存空間長度賦值給numNew  
  5.     int numNew = a.length;  
  6.      // 擴容至size + numNew  
  7.     ensureCapacityInternal(size + numNew);  // Increments modCount  
  8.      // 將a的第0位開始拷貝至elementData的size位開始,拷貝長度爲numNew  
  9.     System.arraycopy(a, 0, elementData, size, numNew);  
  10.      // 將size增加numNew    
  11.     size += numNew;  
  12.      // 如果c爲空,返回false,c不爲空,返回true  
  13.     return numNew != 0;  
  14. }  

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

說明:從第index位開始,將c全部拷貝到ArrayList
源碼解釋:
[java] view plain copy
 print?
  1. public boolean addAll(int index, Collection<? extends E> c) {  
  2.      // 判斷index大於size或者是小於0,如果是,則拋出IndexOutOfBoundsException異常  
  3.     rangeCheckForAdd(index);  
  4.      // 將c轉換爲數組a  
  5.     Object[] a = c.toArray();  
  6.     int numNew = a.length;  
  7.      // 擴容至size + numNew  
  8.     ensureCapacityInternal(size + numNew);  // Increments modCount  
  9.       // 獲取需要添加的個數  
  10.     int numMoved = size - index;  
  11.     if (numMoved > 0)  
  12.         System.arraycopy(elementData, index, elementData, index + numNew,  
  13.                          numMoved);  
  14.     System.arraycopy(a, 0, elementData, index, numNew);  
  15.     size += numNew;  
  16.     return numNew != 0;  
  17. }  

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

說明:根據complement值,將ArrayList中包含c中元素的元素刪除或者保留
源碼解釋:
[java] view plain copy
 print?
  1. private boolean batchRemove(Collection<?> c, boolean complement) {  
  2.     final Object[] elementData = this.elementData;  
  3.      // 定義一個w,一個r,兩個同時右移     
  4.     int r = 0, w = 0;  
  5.     boolean modified = false;  
  6.     try {  
  7.          // r先右移  
  8.         for (; r < size; r++)  
  9.               // 如果c中不包含elementData[r]這個元素  
  10.             if (c.contains(elementData[r]) == complement)  
  11.                   // 則直接將r位置的元素賦值給w位置的元素,w自增  
  12.                 elementData[w++] = elementData[r];  
  13.     } finally {  
  14.         // 防止拋出異常導致上面r的右移過程沒完成  
  15.         if (r != size) {  
  16.               // 將r未右移完成的位置的元素賦值給w右邊位置的元素  
  17.             System.arraycopy(elementData, r,  
  18.                              elementData, w,  
  19.                              size - r);  
  20.               // 修改w值增加size-r  
  21.             w += size - r;  
  22.         }  
  23.         if (w != size) {  
  24.             // 如果有被覆蓋掉的元素,則將w後面的元素都賦值爲null  
  25.             for (int i = w; i < size; i++)  
  26.                 elementData[i] = null;  
  27.             modCount += size - w;  
  28.               // 修改size爲w  
  29.             size = w;  
  30.             modified = true;  
  31.         }  
  32.     }  
  33.     return modified;  
  34. }  

25.removeAll(Collection<?> c)

說明:ArrayList移除c中的所有元素
源碼解釋:
[java] view plain copy
 print?
  1. public boolean removeAll(Collection<?> c) {  
  2.      // 如果c爲空,則拋出空指針異常  
  3.     Objects.requireNonNull(c);  
  4.      // 調用batchRemove移除c中的元素  
  5.     return batchRemove(c, false);  
  6. }  

26.retainAll(Collection<?> c)

說明:和removeAll相反,僅保留c中所有的元素

源碼解釋:

[java] view plain copy
 print?
  1. public boolean retainAll(Collection<?> c) {  
  2.     Objects.requireNonNull(c);  
  3.      // 調用batchRemove保留c中的元素  
  4.     return batchRemove(c, true);  
  5. }  

27.iterator()

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

代碼解釋:

[java] view plain copy
 print?
  1. public Iterator<E> iterator() {  
  2.     return new Itr();  
  3. }  

28.listIterator()

說明:返回一個ListIterator對象,ListItr爲ArrayList的一個內部類,其實現了ListIterator<E> 接口
源碼解釋:
[java] view plain copy
 print?
  1. public ListIterator<E> listIterator() {  
  2.     return new ListItr(0);  
  3. }  

29.listIterator(int index)

說明:返回一個從index開始的ListIterator對象
源碼解釋:
[java] view plain copy
 print?
  1. public ListIterator<E> listIterator(int index) {  
  2.     if (index < 0 || index > size)  
  3.         throw new IndexOutOfBoundsException("Index: "+index);  
  4.     return new ListItr(index);  
  5. }  

30.subList(int fromIndex, int toIndex)

說明:根據兩個參數,獲取到一個子序列
源碼解釋:

[java] view plain copy
 print?
  1. public List<E> subList(int fromIndex, int toIndex) {  
  2.      // 檢查異常  
  3.     subListRangeCheck(fromIndex, toIndex, size);  
  4.      // 調用SubList類的構造方法  
  5.     return new SubList(this0, fromIndex, toIndex);  
  6. }  

內部類

[java] view plain copy
 print?
  1. (1)private class Itr implements Iterator<E>  
  2. (2)private class ListItr extends Itr implements ListIterator<E>  
  3. (3)private class SubList extends AbstractList<E> implements RandomAccess  
  4. (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更深刻的理解。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章