對于ArrayList源碼,我是初次閱讀范舀,可能有很多地方理解不正確,如果有錯(cuò)的話還請大家多多指教了罪。
然后聲明一下的我的JDK版本:openjdk11锭环。(可以直接打開鏈接查看,鏈接的jdk11和我的版本稍有不同泊藕,但基本上一致)
首先說明一下ArrayList變量的含義
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
- DEFAULT_CAPACITY
對于這個(gè)值辅辩,需要說明一點(diǎn),當(dāng)你使用ArrayList<Integer> list = new ArrayList<>();定義一個(gè)list時(shí)娃圆,不是說你不給它賦值玫锋,它默認(rèn)的容量大小就是10了,具體信息會(huì)在add函數(shù)那里說明踊餐。
- EMPTY_ELEMENTDATA
這個(gè)變量主要是當(dāng)elementData.length為0或者size為0時(shí)景醇,為elementData賦一個(gè)空數(shù)組用的。
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA
對于這個(gè)變量吝岭,看著好像和EMPTY_ELEMENTDATA沒什么區(qū)別三痰,但是他倆的作用是不一樣的吧寺,當(dāng)我們不指定initialCapacity時(shí),他會(huì)用DEFAULTCAPACITY_EMPTY_ELEMENTDATA為elementData進(jìn)行初始化散劫,之后會(huì)通過判斷elementData與該變量是不是相等來確定是不是第一次操作稚机。
- elementData
這個(gè)變量應(yīng)該沒什么問題,就是用來存儲(chǔ)數(shù)據(jù)的
- size
這個(gè)變量用于存儲(chǔ)當(dāng)前ArrayList有多少個(gè)元素(注:不是elementData數(shù)組的大谢癫)
- modCount
這個(gè)變量是來自AbstractList赖条,我感覺很有必要說一下,它是用來記錄數(shù)組變化的次數(shù)(添加刪除等操作都算)
add函數(shù)
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length) {
elementData = this.grow();
}
elementData[s] = e;
this.size = s + 1;
}
grow()函數(shù)
private Object[] grow(int minCapacity) {
//使用Arrays.copyOf將原來的數(shù)據(jù)拷貝到新的數(shù)組中
return this.elementData = Arrays.copyOf(this.elementData, this.newCapacity(minCapacity));
}
從這可以看出ArrayList擴(kuò)容還是比較耗時(shí)的常熙,如果一開始能夠確認(rèn)數(shù)組的大小纬乍,那就為他賦一個(gè)初始容量,防止它多次擴(kuò)容而影響性能裸卫。
newCapacity()函數(shù)
private int newCapacity(int minCapacity) {
int oldCapacity = this.elementData.length;
//新生成的容量大小是原來的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
//如果elementData是第一次擴(kuò)容仿贬,那就在minCapaticy和10中選一個(gè)最大值
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);
} else if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
return minCapacity;
}
} else {
//hugeCapacity主要是防止數(shù)組大于2147483639
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
}
}
對于this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA這句我所是第一次擴(kuò)容,其實(shí)也不完全正確墓贿,這里必須是在new一個(gè)ArrayList時(shí)候沒有給它賦初始值茧泪。
上面三段代碼就一塊分析了,整個(gè)添加流程大概如下圖:
這幅圖可能并不是很準(zhǔn)確聋袋,有些細(xì)節(jié)我并沒有體現(xiàn)队伟,因?yàn)榕聢D越來越臃腫。
get()函數(shù)
public E get(int index) {
Objects.checkIndex(index, this.size);
return this.elementData(index);
}
這個(gè)函數(shù)很簡單幽勒,基本沒有要介紹的嗜侮,其中有個(gè)Objects.checkIndex(index, this.size)檢查下標(biāo)是否越界。
hashCode()函數(shù)
public int hashCode() {
int expectedModCount = this.modCount;
int hash = this.hashCodeRange(0, this.size);
//這里不是很確定代嗤,應(yīng)該是防止并發(fā)操作導(dǎo)致數(shù)組更改,導(dǎo)致求出的hash值與當(dāng)前值不一致(下面我也寫了一個(gè)示例)
this.checkForComodification(expectedModCount);
return hash;
}
int hashCodeRange(int from, int to) {
Object[] es = this.elementData;
if (to > es.length) {
throw new ConcurrentModificationException();
} else {
int hashCode = 1;
for(int i = from; i < to; ++i) {
Object e = es[i];
hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
}
return hashCode;
}
}
這里解釋一下hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode())為什么要乘31.
在Effective Java中作者是這樣解釋的:
乘法部分使得散列值依賴與域的順序棘钞,如果一個(gè)類包含多個(gè)相似的域,這樣的乘法運(yùn)算就會(huì)產(chǎn)生一個(gè)更好的散列函數(shù)干毅。例如宜猜,ArrayList中添加了1,2,3,4這四個(gè)元素,另一個(gè)ArrayList也添加了這四個(gè)元素硝逢,但是和第一個(gè)順序不一致姨拥,不用乘法的話,那這兩個(gè)ArrayList的hashCode就是一樣的(這里是我自己的理解渠鸽,下面我也給出了一個(gè)例子)叫乌。之所以選31是因?yàn)樗且粋€(gè)奇素?cái)?shù),如果乘數(shù)是偶數(shù)徽缚,并且乘法溢出的話憨奸,信息就會(huì)丟失,因?yàn)榕c2相乘等價(jià)與移位運(yùn)算凿试,使用素?cái)?shù)的好處并不明顯排宰,但習(xí)慣上都使用素?cái)?shù)來計(jì)算散列結(jié)果似芝。31有個(gè)很好特性,即用移位和減法來代替乘法(
)
- checkForComodification的作用
示例1:
測試結(jié)果:
我一開始起了10個(gè)線程板甘,發(fā)現(xiàn)并沒有出現(xiàn)想要的異常党瓮,于是直接起1000個(gè)線程,不出意外盐类,出現(xiàn)了想要的結(jié)果寞奸。當(dāng)然我也不能100%確定就是這個(gè)作用,但是依照目前測試結(jié)果來看在跳,肯定是有這么個(gè)作用的枪萄。如果大家有別的見解歡迎指出。
- 關(guān)于hashCode的測試
示例1:
測試結(jié)果為true硬毕。
示例2:
結(jié)果為false呻引。
可以知道循序不同,hashCode確實(shí)是不同的吐咳,這也就是乘31所起的作用。
remove函數(shù)
- remove(int index)
public E remove(int index) {
//檢查下標(biāo)是否越界
Objects.checkIndex(index, this.size);
Object[] es = this.elementData;
E oldValue = es[index];
this.fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
++this.modCount;
int newSize;
//如果不大于i元践,說明待刪除的是最后一個(gè)韭脊,直接將其置null
if ((newSize = this.size - 1) > i) {
//如果大于,調(diào)用本地方法進(jìn)行刪除
System.arraycopy(es, i + 1, es, i, newSize - i);
}
es[this.size = newSize] = null;
}
- remove(Object o)
public boolean remove(Object o) {
Object[] es = this.elementData;
int size = this.size;
int i = 0;
//如果待刪除的為null单旁,進(jìn)入if語句
if (o == null) {
while(true) {
//找不到返回false
if (i >= size) {
return false;
}
//找到跳出循環(huán)
if (es[i] == null) {
break;
}
++i;
}
//不為null沪羔,進(jìn)入else(下面同理)
} else {
while(true) {
if (i >= size) {
return false;
}
if (o.equals(es[i])) {
break;
}
++i;
}
}
//找到下標(biāo)后就可以調(diào)用fastRemove了,和上面的remove流程就一樣了
this.fastRemove(es, i);
return true;
}