注:本系列文章中用到的jdk版本均為
java8
ArrayList
類(lèi)圖如下:
ArrayList
的底層是由數(shù)組實(shí)現(xiàn)的竿拆,數(shù)組的特點(diǎn)是固定
大小狞尔,而ArrayList
實(shí)現(xiàn)了動(dòng)態(tài)擴(kuò)容
潘靖。
ArrayList
部分變量如下降淮,在下面的分析中會(huì)用到這些變量。
/**
* 默認(rèn)容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空的對(duì)象數(shù)組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 無(wú)參構(gòu)造器創(chuàng)建的空數(shù)組
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存放數(shù)據(jù)的數(shù)組的緩存變量
*/
transient Object[] elementData;
/**
* 元素?cái)?shù)量
*/
private int size;
一哲思、初始化ArrayList
初始化ArrayList
一般會(huì)使用以下兩個(gè)構(gòu)造器
1.1 無(wú)參構(gòu)造器
初始化ArrayList
的時(shí)候如果不指定大小洼畅,則會(huì)創(chuàng)建一個(gè)空數(shù)組。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
1.2 指定數(shù)組大小的構(gòu)造器
創(chuàng)建一個(gè)預(yù)估大小的數(shù)組棚赔,指定大小后只是指定了數(shù)組初始值的大小帝簇,不影響后面擴(kuò)容,指定的好處就是可以節(jié)省內(nèi)存及時(shí)間上的開(kāi)銷(xiāo)靠益。
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);
}
}
二丧肴、添加元素、動(dòng)態(tài)擴(kuò)容
ArrayList.add(E e)源碼:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add()
中elementData[size++] = e
很好理解胧后,就是將元素插入第size
個(gè)位置芋浮,然后將size++
,我們重點(diǎn)來(lái)看看ensureCapacityInternal(size + 1)
方法壳快;
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
ensureCapacityInternal()
方法中判斷緩存變量elementData
是否為空纸巷,也就是判斷是否是第一次添加元素镇草,如果是第一次添加元素,則設(shè)置初始化大小為默認(rèn)容量10
瘤旨,否則為傳入的參數(shù)梯啤。這個(gè)方法的目的就是獲取初始化數(shù)組容量。獲取到初始化容量后調(diào)用ensureExplicitCapacity(minCapacity)
方法存哲;
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
ensureExplicitCapacity(minCapacity)
方法用來(lái)判斷是否需要擴(kuò)容因宇,假如第一次添加元素,minCapacity
為10
祟偷,elementData
容量為0
察滑,那么就需要去擴(kuò)容。調(diào)用grow(minCapacity)
方法肩袍。
// 數(shù)組的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 擴(kuò)容大小為原來(lái)數(shù)組長(zhǎng)度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 擴(kuò)容容量比需要擴(kuò)容的長(zhǎng)度小,則使用需要擴(kuò)容的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 擴(kuò)容容量比最大數(shù)組長(zhǎng)度大婚惫,則使用最大整數(shù)長(zhǎng)度
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);
}
grow(minCapacity)
方法對(duì)數(shù)組進(jìn)行擴(kuò)容氛赐,擴(kuò)容大小為原數(shù)組的1.5
倍,如果計(jì)算出的擴(kuò)容容量比需要的容量小先舷,則擴(kuò)容大小為需要的容量艰管,如果擴(kuò)容容量比數(shù)組最大容量大,則調(diào)用hugeCapacity(minCapacity)
方法蒋川,將數(shù)組擴(kuò)容為整數(shù)的最大長(zhǎng)度牲芋,然后將elemetData
數(shù)組指向新擴(kuò)容的內(nèi)存空間并將元素復(fù)制到新空間。
當(dāng)需要的集合容量特別大時(shí)捺球,擴(kuò)容1.5
倍就會(huì)非常消耗空間缸浦,因此建議初始化時(shí)預(yù)估一個(gè)容量大小。
三氮兵、刪除元素
ArrayList
提供兩種刪除元素的方法裂逐,可以通過(guò)索引
和元素
進(jìn)行刪除。兩種刪除大同小異泣栈,刪除元素后卜高,將后面的元素一次向前移動(dòng)。
ArrayList.remove(int index)源碼:
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;
}
刪除元素時(shí)南片,首先會(huì)判斷索引是否大于ArrayList
的大小掺涛,如果索引范圍正確,則將索引位置的下一個(gè)元素賦值到索引位置疼进,將ArrayList
的大小-1
薪缆,最后返回移除的元素。操作圖如下伞广,假如我要移除索引為1
的元素:
四矮燎、總結(jié)
ArrayList
底層是數(shù)組實(shí)現(xiàn)的定血,可以進(jìn)行動(dòng)態(tài)擴(kuò)容,擴(kuò)容大小為原來(lái)的1.5倍诞外,雖然可以通過(guò)動(dòng)態(tài)擴(kuò)容澜沟,但是數(shù)組非常大時(shí)會(huì)特別浪費(fèi)空間,因此建議初始化時(shí)預(yù)估數(shù)組大小峡谊。ArrayList
允許插入重復(fù)值和空值茫虽。ArrayList
實(shí)現(xiàn)了RandomAccess
接口,支持快速隨機(jī)訪(fǎng)問(wèn)既们,就是可以通過(guò)索引快速查到某個(gè)元素濒析,因此遍歷時(shí)使用for循環(huán)的方式效率更高。ArrayList
是線(xiàn)程不安全的啥纸,可以通過(guò)Collections.synchronizedList
將其轉(zhuǎn)變?yōu)榫€(xiàn)程安全的集合号杏,不過(guò)一般不會(huì)使用,Vector
和CopyOnWriteArrayList
是線(xiàn)程安全的斯棒,Vector
性能一般盾致,逐漸被CopyOnWriteArrayList
取代了。
點(diǎn)關(guān)注荣暮、不迷路
如果覺(jué)得文章不錯(cuò)庭惜,歡迎關(guān)注、點(diǎn)贊穗酥、收藏护赊,你們的支持是我創(chuàng)作的動(dòng)力,感謝大家砾跃。
如果文章寫(xiě)的有問(wèn)題骏啰,請(qǐng)不要吝惜文筆,歡迎留言指出抽高,我會(huì)及時(shí)核查修改器一。
如果你還想看到更多別的東西,可以微信搜索「Java旅途」進(jìn)行關(guān)注厨内∑盹酰「Java旅途」目前已經(jīng)整理各種中間件的使用教程及各類(lèi)Java相關(guān)的面試題。