注:博主java集合框架源碼剖析系列的源碼所有基於JDK1.8.0版本。java
本博客將從源碼角度帶領你們學習關於ArrayList的知識。api
一ArrayList類的定義:數組
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable從上述代碼能夠看到:ArrayList繼承自AbstractList同時實現了List,RandomAccess,Cloneable與Serializable接口
二ArrayList類一些重要屬性:數據結構
private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; private int size;從這裏能夠看到ArrayList所有操做是基於Object[]數據結構的,即ArrayList底層是基於數組實現的,所以具有較好的隨機訪問的能力
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); } } public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 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; } }能夠看到當用一個集合做爲參數去初始化一個ArrayList的時候,其內部處理過程與LinkedList很是相似,即先將集合轉化爲數組,而後將元素複製到本身內部定義的數組elementData數組中。返回的若不是Object[]將調用Arrays.copyOf方法將其轉爲Object[]。Arrays.copyOf代碼以下:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") 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; }
1add(E e)與add(int index,E element)框架
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }而在該函數中調用了ensureExplicitCapacity(int minCapacity)函數,咱們來看一下其源碼:
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0)// 超出了數組可容納的長度,則進行動態擴展,即調用grow函數 grow(minCapacity); }
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);//將oldCapacity除以2在加上本身,即新的容量爲原來的1.5倍 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); }綜上,當調用add()添加元素時,add方法先調用ensureCapacityInternal方法增長自身容量(注意此時僅僅是minCapicity的值加1,而數組內存還未擴容),本質上是經過grow()函數來完成的,在調用grow()函數時,先求出原數組的長度記爲oldCapacity,而後將該值更新爲oldCapacity+oldCapacity>>1即新的容量爲原來容量的1.5倍.,將其記爲newCapacity,若是擴容後的容量超出了數組可容納的最大長度MAX_ARRAY_SIZE,則調用hugeCapacity()將其返回值 Integer.MAX_VALUE做爲新的容量,而後調用系統的arraycopy函數將原數組的內容複製到新數組中返回。
即add()方法中存在擴容機制,當經過add()方法添加一個元素時,會先調用ensureCapacityInternal方法來確保數組內存始終足夠,本質上是經過grow函數來完成的,在grow()函數中會將原數組的長度更改成其原來的1.5倍,若是更改後的數組容量比傳入的minCapacity小,則將更改後的容量更新爲minCapacity(該值是指望的數組的最小容量,如當已存在1個元素而後添加一個元素則minCapacity的值爲2即能容納添加進去的元素的最小容量,擴容後的值newCapacity在絕大多數狀況下比它大,但可能比它小,由於上述擴容機制採用的是擴大爲1.5倍,而採用的是整除,如1+1/2=1,而minCapacity=size+1=2,此時newCapacity<minCapacity)dom
2addAll(Collection<? extends E> c) 與addAll(int index, Collection<? extends E> c) 函數
public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } public boolean addAll(int index, Collection<? extends E> c) { 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; }能夠看到,addAll()與add()方法幾乎徹底相同,只不過存在一個將集合轉化爲數組的過程,這一點與LinkedList很是相似,而後接下來的操做與add操做徹底相同,即
調用ensureCapacityInternal擴容,若是插入位置不是Arraylist的末尾,則調用系統的arraycopy函數將索引處及其後的元素後移一位在index處插入該元素。處理完畢後將臨時數組a中的元素arraycopy到elementData中。學習
3Object[] toArray() this
public Object[] toArray() { return Arrays.copyOf(elementData, 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; }該函數的做用就是返回一個包含list中全部元素的數組,具體實現過程以下:
若是傳入數組的長度length小於size,返回一個新的數組,大小爲size,類型與傳入數組相同;
不然將elementData的元素所有複製到傳入的數組a中,並返回a;
若length大於size,除了複製elementData外,還將把返回數組的第size個元素置爲空。
4void sort(Comparator<? super E> c)spa
public void sort(Comparator<? super E> c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, size, c); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; }
Comparator接口的。
五總結:
1ArrayList的內部是基於Object[]類型的數組實現的,支持下標訪問,所以具有較高的隨機訪問速度。
2當用一個集合做爲參數來構造一個ArrayList時,其內部是是先將該集合轉化爲數組,這一點與LinkedList很是相似,與HashMap同樣,ArrayList也存在擴容機制,即調用ensureCapacityInternal擴容
3能夠看到ArrayList中的方法都未使用synchronized關鍵字修飾,即ArrayList是非同步的。
4ArrayList容許重複元素存在,由於在add元素的過程當中不存在HashMap中put時判重替換的過程,只是進行簡單的插入操做。