Java 1.8 ArrayList源碼解析
1 // 非線程安全 2 // 繼承了AbstractList類 3 // 實現了List、RandomAccess、Cloneable、java.io.Serializable接口 4 // 後面3個接口是標記接口,沒有抽象方法。 5 // 表示ArrayList可以隨機訪問、淺復制、序列化和反序列化。 6 public class ArrayList<E> extends AbstractList<E> 7 implements List<E>, RandomAccess, Cloneable, java.io.Serializable
1 // 序列化版本唯一標識符,版本向上兼容。2 private static final long serialVersionUID = 8683452581122892189L;
1 // 默認初始容量 2 private static final int DEFAULT_CAPACITY = 10;
1 // 容量為0的空數組 2 private static final Object[] EMPTY_ELEMENTDATA = {};
1 // 默認容量的空數組,向空數組中插入第1個元素時該數組可用於判斷初始的容量是多少(0或10)從而確定擴容的大小。 2 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
1 // 瞬態的緩沖數組 使用transient關鍵字目的是只序列化實際存儲的那些元素,而不是整個數組。 2 transient Object[] elementData; // non-private to simplify nested class access
1 // 數組實際存儲的元素數量 2 private int size;
1 // 帶初始容量的構造函數 2 public ArrayList(int initialCapacity) { 3 if (initialCapacity > 0) { 4 this.elementData = newObject[initialCapacity]; 5 } else if (initialCapacity == 0) { 6 // 指向空數組 7 this.elementData = EMPTY_ELEMENTDATA; 8 } else { 9 throw new IllegalArgumentException("Illegal Capacity: "+ 10 initialCapacity); 11 } 12 }
1 // 無參構造函數 2 public ArrayList() { 3 // 指向默認容量為10的空數組 4 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 5 }
1 // Collection對象作為參數的構造函數 2 public ArrayList(Collection<? extends E> c) { 3 elementData = c.toArray(); 4 if ((size = elementData.length) != 0) { 5 // c.toArray might (incorrectly) not return Object[] (see 6260652) 6 // 返回的數組類型可能不是Object[],要看運行時類型(實際類型),例如String[]。這樣會導致向下轉型,例如修改元素時指向了非繼承或實現關系的對象時會出錯,String->Integer。 7 // 原因在於裏面存儲的元素可能不是Object類型。 8 if (elementData.getClass() != Object[].class) 9 // 重新指向Object數組。 10 // Arrays.copyOf會創建新數組,返回引用。 11 elementData = Arrays.copyOf(elementData, size, Object[].class); 12 } else { 13 // replace with empty array. 14 this.elementData = EMPTY_ELEMENTDATA; 15 } 16 }
1 // 將數組長度削減到元素數量對應的長度。 2 public void trimToSize() { 3 modCount++; // 快速失敗機制 4 if (size < elementData.length) { // 如果實際元素數量小於數組長度 5 elementData = (size == 0) 6 ? EMPTY_ELEMENTDATA 7 : Arrays.copyOf(elementData, size); 8 } 9 }
fail-fast機制
fail-fast機制也叫作”快速失敗”機制,是Java集合中的一種錯誤檢測機制。
在對集合進行叠代過程中,除了叠代器可以對集合進行數據結構上進行修改,其他的對集合的數據結構進行修改,都會拋出ConcurrentModificationException錯誤。
這裏,所謂的進行數據結構上進行修改,
是指對存儲的對象,進行add、set、remove操作,進而對數據發生改變。
ArrayList中,有個modCount的變量,每次進行add、set、remove等操作,都會執行modCount++。
在獲取ArrayList的叠代器時,會將ArrayList中的modCount保存在叠代中,每次執行add,set,remove等操作,都會執行一次檢查,調用checkForComodification方法,對modCount進行比較。如果叠代器中的modCount和List中的modCount不同,則拋出ConcurrentModificationException。
1 // 公有方法,保證容量。 2 public void ensureCapacity(int minCapacity) { 3 // 最小增量 4 int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 5 // any size if not default element table 6 ? 0 7 // larger than default for default empty table. It‘s already 8 // supposed to be at default size. 9 : DEFAULT_CAPACITY; 10 11 // 如果最小容量大於最小增量則確保最小容量足夠。 12 if (minCapacity > minExpand) { 13 ensureExplicitCapacity(minCapacity); 14 } 15 }
1 // 確保內部的容量足夠。 2 private void ensureCapacityInternal(int minCapacity) { 3 // 如果elementData指向的是默認容量的空數組則說明當前容量為10。 4 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 5 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 6 } 7 8 // 確保最小容量足夠。 9 ensureExplicitCapacity(minCapacity); 10 }
1 // 確保最小容量足夠。 2 private void ensureExplicitCapacity(int minCapacity) { 3 modCount++; // 快速失敗機制 4 5 // overflow-conscious code 6 // 如果最小容量大於數組長度則擴容。 7 if (minCapacity - elementData.length > 0) 8 grow(minCapacity); 9 }
1 /** 2 * The maximum size of array to allocate. 3 * Some VMs reserve some header words in an array. 4 * Attempts to allocate larger arrays may result in 5 * OutOfMemoryError: Requested array size exceeds VM limit 6 */ 7 // 可分配的數組最大容量 8 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
1 // 擴容 2 private void grow(int minCapacity) { // minCapacity是需要達到的最小容量。 3 // overflow-conscious code 4 // 原來的容量是數組長度。 5 int oldCapacity = elementData.length; 6 // 新的容量是1.5倍的原有容量。 7 int newCapacity = oldCapacity + (oldCapacity >> 1); 8 9 // 如果最小容量大於1.5倍的原有容量則最小容量設為新的容量。 10 if (newCapacity - minCapacity < 0) 11 newCapacity = minCapacity; 12 // 如果新的容量大於可分配的數組最大容量 13 if (newCapacity - MAX_ARRAY_SIZE > 0) 14 // minCapacity is usually close to size, so this is a win: 15 newCapacity = hugeCapacity(minCapacity); 16 17 // elementData指向擴容後的新數組。 18 elementData = Arrays.copyOf(elementData, newCapacity); 19 }
1 // 確保數組容量最大為Integer.MAX_VALUE。 2 private static int hugeCapacity(int minCapacity) { 3 // 如果最小容量溢出則拋出內存不足錯誤。 4 if (minCapacity < 0) // overflow 5 throw new OutOfMemoryError(); 6 7 // 如果最小容量超過了可分配的數組最大容量則返回整形的最大值作為新容量,否則返回可分配的數組最大容量作為新容量。 8 return (minCapacity > MAX_ARRAY_SIZE) ? 9 Integer.MAX_VALUE : 10 MAX_ARRAY_SIZE; 11 }
1 // 獲取實際元素數量。 2 public int size() { 3 return size; 4 }
1 // 判斷數組是否為空即判斷是否存在實際元素。 2 public boolean isEmpty() { 3 return size == 0; 4 }
1 // 判斷是否包含某個元素,通過該元素對應的下標來判斷。 2 public boolean contains(Object o) { 3 return indexOf(o) >= 0; 4 }
1 // 順序獲取某個元素對應的下標。 2 public int indexOf(Object o) { 3 if (o == null) { // 如果該元素為空 4 for (int i = 0; i < size; i++) 5 if (elementData[i] == null) 6 return i; 7 } else { // 否則 8 for (int i = 0; i < size; i++) 9 if (o.equals(elementData[i])) 10 return i; 11 } 12 13 // 沒有找到該元素。 14 return -1; 15 }
1 // 逆序獲取某個元素對應的下標。 2 public int lastIndexOf(Object o) { 3 if (o == null) { // 如果該元素為空 4 for (int i = size-1; i >= 0; i--) 5 if (elementData[i] == null) 6 return i; 7 } else { // 否則 8 for (int i = size-1; i >= 0; i--) 9 if (o.equals(elementData[i])) 10 return i; 11 } 12 13 // 沒有找到該元素。 14 return -1; 15 }
1 // 淺復制,沒有復制數組中元素作為引用指向的對象。該方法返回的是當前ArrayList對象的淺復制對象引用。 2 public Object clone() { 3 try { 4 // 調用父類復制方法。 5 ArrayList<?> v = (ArrayList<?>) super.clone(); 6 // v.elementData指向存儲原來對象(元素)的新數組。 7 v.elementData = Arrays.copyOf(elementData, size); 8 v.modCount = 0; // 快速失敗機制 9 10 return v; // 返回引用。 11 } catch (CloneNotSupportedException e) { 12 // this shouldn‘t happen, since we are Cloneable 13 throw new InternalError(e); 14 } 15 }
1 // 返回一個數據拷貝後的Object數組。 2 public Object[] toArray() { 3 return Arrays.copyOf(elementData, size); 4 }
1 // 按適當順序(從第一個到最後一個元素)返回包含此列表中所有元素的數組;返回數組的運行時類型是指定數組的運行時類型。 2 @SuppressWarnings("unchecked") 3 public <T> T[] toArray(T[] a) { 4 if (a.length < size) 5 // Make a new array of a‘s runtime type, but my contents: 6 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); 7 8 System.arraycopy(elementData, 0, a, 0, size); 9 10 if (a.length > size) 11 a[size] = null; 12 13 return a; 14 }
1 // 按下標隨機訪問元素。 2 @SuppressWarnings("unchecked") 3 E elementData(int index) { 4 return (E) elementData[index]; 5 }
1 // 通過參數檢查之後按下標隨機訪問元素。 2 public E get(int index) { 3 rangeCheck(index); 4 5 return elementData(index); 6 }
1 // 通過參數檢查之後按下標修改元素內容並返回原來的元素內容。 2 public E set(int index, E element) { 3 rangeCheck(index); 4 5 E oldValue = elementData(index); 6 elementData[index] = element; 7 8 return oldValue; 9 }
1 // 末尾增加元素。 2 public boolean add(E e) { 3 // 確保容量足夠。 4 ensureCapacityInternal(size + 1); // Increments modCount!! 5 elementData[size++] = e; 6 7 return true; 8 }
1 // 按下標增加元素。可能需要向後移動。 2 public void add(int index, E element) { 3 // 下標檢查。 4 rangeCheckForAdd(index); 5 6 // 確保容量足夠。 7 ensureCapacityInternal(size + 1); // Increments modCount!! 8 // 下標大於index的部分向後移。註意size>=index。 9 System.arraycopy(elementData, index, elementData, index + 1, 10 size - index); 11 elementData[index] = element; // 插入新元素。 12 size++; // 實際元素數量增加。 13 }
1 // 按下標刪除元素並返回原來的元素內容。 2 public E remove(int index) { 3 // 下標檢查。 4 rangeCheck(index); 5 6 modCount++; // 快速失敗機制 7 E oldValue = elementData(index); 8 9 // 可能需要向前移動。註意size>=index。所以numMoved可能小於0。 10 int numMoved = size - index - 1; 11 if (numMoved > 0) 12 System.arraycopy(elementData, index+1, elementData, index, 13 numMoved); 14 elementData[--size] = null; // clear to let GC do its work 15 16 return oldValue; 17 }
1 // 移除此列表中首次出現的指定元素(如果存在)。 2 public boolean remove(Object o) { 3 if (o == null) { // 如果是空元素 4 for (int index = 0; index < size; index++) 5 if (elementData[index] == null) { 6 fastRemove(index); // 不返回舊值。 7 return true; 8 } 9 } else { // 否則 10 for (int index = 0; index < size; index++) 11 if (o.equals(elementData[index])) { 12 fastRemove(index); // 不返回舊值。 13 return true; 14 } 15 } 16 17 // 沒有找到該元素。 18 return false; 19 }
1 // 根據下標刪除元素,但不返回舊值。 2 private void fastRemove(int index) { 3 modCount++; // 快速失敗機制 4 5 // 可能需要向前移動。註意size>=index。所以numMoved可能小於0。 6 int numMoved = size - index - 1; 7 if (numMoved > 0) 8 System.arraycopy(elementData, index+1, elementData, index, 9 numMoved); 10 elementData[--size] = null; // clear to let GC do its work 11 }
1 // 移除此列表中的所有元素。 2 public void clear() { 3 modCount++; // 快速失敗機制 4 5 // clear to let GC do its work 6 for (int i = 0; i < size; i++) 7 elementData[i] = null; 8 9 size = 0; 10 }
1 // 將指定collection中的所有元素添加到此列表的尾部。 2 public boolean addAll(Collection<? extends E> c) { 3 // 得到c對應的Object[]數組。 4 Object[] a = c.toArray(); 5 // 獲取新增的元素數量。 6 int numNew = a.length; 7 // 確保容量足夠。 8 ensureCapacityInternal(size + numNew); // Increments modCount 9 // 將新的元素添加在尾部。elementData指向的數組裏面存儲的都是Object類型元素。 10 System.arraycopy(a, 0, elementData, size, numNew); 11 // 增加實際元素數量。 12 size += numNew; 13 14 return numNew != 0; 15 }
1 // 從指定的位置開始,將指定 collection 中的所有元素插入到此列表中。 2 public boolean addAll(int index, Collection<? extends E> c) { 3 // 下標檢查。 4 rangeCheckForAdd(index); 5 6 // 得到c對應的Object[]數組。 7 Object[] a = c.toArray(); 8 // 獲取新增的元素數量。 9 int numNew = a.length; 10 // 確保容量足夠。 11 ensureCapacityInternal(size + numNew); // Increments modCount 12 13 // 可能需要向前移動。註意size>=index。所以numMoved可能小於0。 14 int numMoved = size - index; 15 if (numMoved > 0) 16 System.arraycopy(elementData, index, elementData, index + numNew, 17 numMoved); 18 19 // 從index開始添加新元素。 20 System.arraycopy(a, 0, elementData, index, numNew); 21 // 增加實際元素數量。 22 size += numNew; 23 24 return numNew != 0; 25 }
1 // 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之間的所有元素。 2 protected void removeRange(int fromIndex, int toIndex) { 3 modCount++; // 快速失敗機制 4 // 可能需要向前移動。 5 int numMoved = size - toIndex; // 元素移動數量 6 System.arraycopy(elementData, toIndex, elementData, fromIndex, 7 numMoved); 8 9 // clear to let GC do its work 10 // newSize是新的實際元素數量。 11 int newSize = size - (toIndex-fromIndex); 12 for (int i = newSize; i < size; i++) { 13 elementData[i] = null; 14 } 15 size = newSize; // 更新實際元素數量。 16 }
1 // 下標上界範圍檢查。 2 private void rangeCheck(int index) { 3 if (index >= size) 4 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 5 }
1 // 增加元素時下標範圍檢查。 2 private void rangeCheckForAdd(int index) { 3 if (index > size || index < 0) 4 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 5 }
1 // 輸出越界異常提示。 2 private String outOfBoundsMsg(int index) { 3 return "Index: "+index+", Size: "+size; 4 }
1 // 序列化,elementData寫入輸出流。 2 private void writeObject(java.io.ObjectOutputStream s) 3 throws java.io.IOException{ 4 // Write out element count, and any hidden stuff 5 int expectedModCount = modCount; 6 // 版本向上兼容,舊版本的反序列化值在新版本中序列化會增加新屬性的默認值。 7 s.defaultWriteObject(); 8 9 // Write out size as capacity for behavioural compatibility with clone() 10 // 版本向上兼容,舊版本需要讀取數組長度length。如果不寫入size,會導致舊版本反序列化時出現異常。 11 s.writeInt(size); 12 13 // Write out all elements in the proper order. 14 for (int i=0; i<size; i++) { 15 s.writeObject(elementData[i]); 16 } 17 18 // 快速失敗機制 19 if (modCount != expectedModCount) { 20 throw new ConcurrentModificationException(); 21 } 22 }
1 // 反序列化,elementData從輸入流中讀取。 2 private void readObject(java.io.ObjectInputStream s) 3 throws java.io.IOException, ClassNotFoundException { 4 // elementData指向空數組。 5 elementData = EMPTY_ELEMENTDATA; 6 7 // Read in size, and any hidden stuff 8 // 版本向上兼容,舊版本的序列化值在新版本中反序列化時會自動忽略新屬性。 9 s.defaultReadObject(); 10 11 // Read in capacity 12 s.readInt(); // ignored 13 14 // size是通過ObjectOutputStream類中defaultWriteFields方法和ObjectInputStream類中defaultReadFields方法得到的。 15 // 如果存在元素 16 if (size > 0) { 17 // be like clone(), allocate array based upon size not capacity 18 // 確保容量足夠。 19 ensureCapacityInternal(size); 20 21 Object[] a = elementData; 22 // Read in all elements in the proper order. 23 for (int i=0; i<size; i++) { 24 a[i] = s.readObject(); 25 } 26 } 27 }
ObjectOutputStream會調用ArrayList類的writeObject方法進行序列化,ObjectInputStream會調用相應的readObject方法進行反序列化。通過反射機制,ObjectOutputStream的writeObject會根據傳進來的ArrayList對象得到Class,然後再包裝成ObjectStreamClass,在writeSerialData方法裏,會調用ObjectStreamClass的 invokeWriteObject方法,從而調用ArrayList類中的writeObject方法。
1 // 排序 2 @Override 3 @SuppressWarnings("unchecked") 4 public void sort(Comparator<? super E> c) { 5 final int expectedModCount = modCount; 6 Arrays.sort((E[]) elementData, 0, size, c); 7 // 快速失敗機制 8 if (modCount != expectedModCount) { 9 throw new ConcurrentModificationException(); 10 } 11 modCount++; 12 }
參考資料
ArrayList中elementData為什麽被transient修飾?
Java筆記---c.toArray() might (incorrectly) not return object[] (see 6260652)官方Bug
java ArrayList的序列化分析
ArrayList中size為什麽要序列化兩次?
ArrayList概念及手寫代碼
Java集合---ArrayList的實現原理
java8 ArrayList源碼閱讀
jdk1.8.0_45源碼解讀——ArrayList的實現
java.util.List.toArray() 使用體會
Java 1.8 ArrayList源碼解析