ArrayList 底層源碼詳解
定義: 一個實現了List接口的可調整大小的數組,實現了List接口所有的可選操作並且可以容納所有類型的對象包括null,同時提供了方法來改變List容量的大小。
特徵:
- 1 可自動擴容,和vector相比是非線程安全的。
- 2 可以通過List list = Collections.synchronizedList(new ArrayList(…));生成線程安全的List對象
- 3 ArrayList實現了Iterator接口可以通過iterator()方法獲取到迭代器
- 4 由於ArrayList是非線程安全的,所以如果同時它進行遍歷和該變它的內部接口會throw ConcurrentModificationException。(可以通過iterator解決這個問題)
底層實現
實現結構圖
![]()
具體底層代碼實現
定義的一些常量
/**
* jdk1.8中ArrayList的默認容量大小爲10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 當ArrayList初始化爲0時默認賦值爲該空對象數組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 當ArrayList初始化時不指定大小時侯使用該對象數組進行初始化,同時用來區分初始化爲0的那種情況
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存儲ArrayList元素的數組緩衝區。ArrayList元素的底層存儲都是基於該數組
*/
transient Object[] elementData;
/**
* 用來記錄ArrayList中元素的個數
*/
private int size;
常用方法的底層實現
/**
* 初始化一個容量爲10的空的ArrayList集合
*/
public ArrayList(){
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 參數initialCapacity代表ArrayList指定的初始化容量的大小
* initialCapacity>0 初始化Object[] elementData數組大小爲指定的initialCapacity
* initialCapacity=0 初始化Object[] elementData 爲空數組EMPTY_ELEMENTDATA
* initialCapacity<0 拋出容量大小不合法的異常
*/
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);
}
}
/**
* 參數 任意Collection對象
* 1 將Collection對象轉換成數組
* 2 如果轉換成的數組size==0 則創建一個容量爲0的ArrayList對象,否則將對象數組拷貝到elementData數組中去
* 注意: toArray()方法返回的可能不是Object[]類型,例如Arrays.asList("asList")底層返回的就是一個String[]類型的數組,只是包裝在List<String>中。,所以這邊判斷了elementData.getClass()的類型,如果不是對象數組將它向上轉型成了對象數組。
*/
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;
}
}
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;
}
/**
* E e代表可以添加的參數 如果實例化的時候指定了泛型類型則只能添加指定類型的元素,不然則不會進行類型檢查,e可以是任意類型的元素在編譯的時候會自動類型擦除當成Object來處理
* 添加元素到集合中去
*
* ensureCapacityInternal(size+1)
* 添加一個元素的時候確保容量足夠大
*
* elementData[size++] = e;
* 將元素添追加到elmentData數組的最後一位
*
* 返回boolean爲true
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 確保添加的時候集合的容量足夠大
*
* 調用ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));方法進行擴容
*
* 調用calculateCapacity(elementData, minCapacity)計算集合需要達到的最小的容量
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 判斷elementData是否爲默認容量的數組,如果是則選取默認容量和添加元素後容量中的最大值
* 如果不是直接返回minCapacity(需要達到的最小容量)
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 確保容量的具體方法
*
* modCount++; 用來記錄每一次結構性的調整 modCount繼承與abstractList
*
* 判斷添加元素後的容量是否大於當前集合的容量,如果超過了當前集合的容量就調用grow(minCapacity)進行擴容
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 將容量擴展爲當前容量的1.5倍(利用位運算)
*
* 如果擴大1.5後仍然比當前需要的容量小,就擴容至minCapcity大小
* 如果擴大1.5後超過了MAX_ARRAY_SIZE 調用hugeCapacity(minCapacity);獲取指定容量
*
* 將當前數組複製給新的擴容後的數組作爲新的集合
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
/**
* 如果需要容量大小0 拋出異常
* 不然判斷是否大於MAX_ARRAY_SIZE 如果大於則返回Inter.MAX_VALUE 沒有則返回MAX_ARRAY_SIZE
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
- add(int index, E element)方法(在指定位置插入元素,即將當前元素以及往後的元素向後移動,將元素插入該位置)
/**
* 1. 檢查索引位置是否合法
* 2. 確保容量足夠大(這部和add()方法判斷一樣)(這步 modCount +1 因爲改變了數據結構) ensureCapacityInternal(size + 1);
* 3. 將index後的內容向後移動一位System.arraycopy(elementData, index, elementData, index + 1,size - index);
* 3. 將元素添加到index的位置 elementData[index] = element;
* 4. size++; 元素的個數加一
*
public void add(int index, E element) {
rangeCheckForAdd(index);
// Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 判斷如果索引比元素個數大或者小於0 就拋出異常索引超過邊界的異常
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
注意有兩一種情況,如果index==size則表示在當前集合的最後添加一個元素,此時和add()方法效果一樣。
- remove(int index) 將指定索引位置的元素刪除 返回值爲被刪除的元素
/**
* 1.檢查索引位置是否合法 rangeCheck(index);
* 2. modCount++; 數據結構被修改記錄+1
* 3. E oldValue = elementData(index);獲取索引位置的元素
* 4. 計算需要向前移動的元素的個數然後將元素向前拷貝
* 5. 將原本最後一位的元素釋放(size-1並且賦值爲null)
* 6. 返回刪除的元素
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
/**
* 判讀index是否大於等於元素個數,滿足則拋出經常索引位置越界
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
- remove(Object o) 將集合中指定元素刪除 返回值爲boolean
/*
* 1. 分兩種情況 如果o==null 則通過elementData[index] == null查找是否存爲null的元素
o!=null 則通過o.equals(elementData[index]查找是否存爲null的元素(考慮到對象和值對於值來說就是值是否相等,對象則需要是同一個對象地址纔會被刪除)
*/
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;
}
/**
* 1.modCount++ 修改了集合的數據結構
* 2.計算需要向前移動的元素的數量
* 3.拷貝元素
* 4.將原本最後一位元素賦值爲null
* 注意 此處刪除並沒有檢查索引是否合法 因爲只有元素存在纔會進行這一步索引保證了索引的合法性
*/
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
}
- set(int index, E element)(在容器的指定位置設置指定的值)
/**
* 1 檢查索引位置是否合法
* 2 將該位置舊的值賦值給臨時的變量oldValue
* 3 將新值存放到該索引位置
* 4 返回被刪除的值
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
- get(int index)獲取指定索引位置的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}