ArrayList
繼承於AbstractList,底層是數組實現的
transient Object[] elementData;
不設置大小則默認大小爲0,當第一次進行add操作時候會擴容,如果容量小於10,默認擴容爲10
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 看ensureCapacityInternal
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));// 看calculateCapacity
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
return Math.max(DEFAULT_CAPACITY, minCapacity); // DEFAULT_CAPACITY = 10,所以返回10
}
return minCapacity;
}
如果不是初始化擴容,則每次擴容後新數組的長度爲原數組的1.5倍,源碼如下:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);// 變爲原數組的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);
}
思考:當時用增強for循環的過程中remove一個元素,會報錯麼?
分兩種情況:
一、remove非倒數第二個元素會報異常,ConcurrentModificationException
二、remove倒數第二個元素不會報錯
分析如下:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);// 看這個方法
return true;
}
} else {
...
}
return false;
}
private void fastRemove(int index) {
modCount++;//這裏modCount進行自增加1(modCount是判斷修改了多少次的計數器)
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 這裏size自減1
}
由於使用的是增強for循環,遍歷元素是通過Iterator迭代器來實現的,則此時會調用Itr內部類的hasNext方法以及next,我們來看這兩個方法:
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();// 重點!
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
在調用next方法時會調用checkForComodification方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
可以看出該方法是檢查修改次數modCount是否等於expectedModCount的,在上面ArrayList的remove方法中,操作後會導致modCount自增1,
而expectedModCount並沒有增加,所以就會拋出ConcurrentModificationException異常。
那麼爲什麼remove倒數第二個的時候不會拋出異常呢?
因爲在執行next方法之前會執行hasNext方法,該方法會判斷cursor(當前元素數組下標+1)是否等於size,不等於說明還有元素則返回true,反之返回false,看上面的fastRemove方法,remove完成後size已經自減1,所以如果remove倒數第二個元素那麼這時cursor是等於size的,因此hasNext方法返回false,就不會執行next方法,也就不會報錯,當刪除的元素非倒數第二個元素時,cursor != size就會返回true,所以會執行next方法,會報錯。
Vector
底層數據結構是數組,查詢快,增刪慢。 線程安全,效率低,remove操作同樣具有上述問題,新建該集合時,如果不指定長度,默認是10,每次擴容默認會在原來的基礎上擴大1倍,也可以配置參數指定每次擴容的大小。
LinkedList
底層數據結構是鏈表,查詢慢,增刪快。 線程不安全,效率高,remove操作同樣具有上述問題。