一 成員變量解析
/**
* 默認(rèn)初始化容量.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* ArrayList存儲(chǔ)數(shù)據(jù)的數(shù)組.
* ArrayList的容量就是該數(shù)組的長(zhǎng)度
* 如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* 那么當(dāng)?shù)匾粋€(gè)元素加進(jìn)來時(shí),elementData 的長(zhǎng)度會(huì)擴(kuò)充為DEFAULT_CAPACITY.
*/
transient Object[] elementData;
/**
* 數(shù)組的容量
*/
private int size;
/**
* 數(shù)組可申請(qǐng)的最大長(zhǎng)度
* 有些虛擬機(jī)需要在數(shù)組中保存頭部信息
* 申請(qǐng)長(zhǎng)度超出范圍會(huì)引起
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
二 關(guān)鍵方法解析
1 添加元素
public boolean add(E e) {
// 增加ArrayList的容量(擴(kuò)容敢靡,后邊詳述)
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
2 擴(kuò)容
ArrayList默認(rèn)大小是10桃移,如果底層數(shù)組大小不夠了怎么辦见转,答案是進(jìn)行擴(kuò)容誊稚,這就是為何說ArrayList的底層是基于動(dòng)態(tài)數(shù)組實(shí)現(xiàn)的原因管削。
private void ensureCapacityInternal(int minCapacity) {
/*
* 如果elementData還是默認(rèn)的空數(shù)組倒脓,
* 那么會(huì)設(shè)置elementData的最小容量,
* 最小容量為DEFAULT_CAPACITY和minCapacity的最大值
*/
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果最小容量大于elementData長(zhǎng)度則擴(kuò)容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 設(shè)置newCapacity 為oldCapacity 的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果newCapacity小于minCapacity 含思,則設(shè)newCapacity=minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果newCapacity超出數(shù)組可申請(qǐng)的最大長(zhǎng)度則重新設(shè)置newCapacity
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
可以看到在每次擴(kuò)容時(shí)新的容量大小是舊容量大小的1.5倍崎弃,最后會(huì)調(diào)用Arrays的copyOf方法將舊數(shù)組的內(nèi)容復(fù)制到新數(shù)組中。
3 獲取元素
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
/**
* 檢查index是否越界含潘,該方法并不檢查index是否為負(fù)數(shù)
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
4 替換特定位置的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
5 插入元素
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++;
}
看到在插入元素的時(shí)候饲做,System.arraycopy方法將array中從index起始的子集復(fù)制到從index+1開始的地方,然后再index出加入新插入的元素遏弱。
6 刪除元素
刪除元素有兩種方式:
- 根據(jù)index刪除和根據(jù)
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;
}
- 根據(jù)元素o刪除盆均,這會(huì)刪除arrayList中第一個(gè)和o匹配的元素
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;
}
三 ArrayList的優(yōu)缺點(diǎn)
從上面的幾個(gè)過程總結(jié)一下ArrayList的優(yōu)缺點(diǎn)。ArrayList的優(yōu)點(diǎn)如下:
- ArrayList底層以數(shù)組實(shí)現(xiàn)漱逸,是一種隨機(jī)訪問模式泪姨,再加上它實(shí)現(xiàn)了RandomAccess接口,因此查找也就是get的時(shí)候非呈问悖快
- ArrayList在順序添加一個(gè)元素的時(shí)候非常方便肮砾,只是往數(shù)組里面添加了一個(gè)元素而已
不過ArrayList的缺點(diǎn)也十分明顯:
- 刪除元素的時(shí)候,涉及到一次元素復(fù)制袋坑,如果要復(fù)制的元素很多仗处,那么就會(huì)比較耗費(fèi)性能
- 插入元素的時(shí)候,涉及到一次元素復(fù)制咒彤,如果要復(fù)制的元素很多疆柔,那么就會(huì)比較耗費(fèi)性能
因此,ArrayList比較適合順序添加镶柱、隨機(jī)訪問的場(chǎng)景旷档。
四 ArrayList和Vector的區(qū)別
- Vector是線程安全的,ArrayList是線程非安全的
- Vector可以指定增長(zhǎng)因子歇拆,如果該增長(zhǎng)因子指定了鞋屈,那么擴(kuò)容的時(shí)候會(huì)每次新的數(shù)組大小會(huì)在原數(shù)組的大小基礎(chǔ)上加上增長(zhǎng)因子;如果不指定增長(zhǎng)因子故觅,那么就給原數(shù)組大小*2厂庇,源代碼是這樣的:
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
五 為什么ArrayList的elementData是用transient修飾的?
不知道大家有沒有想過输吏,為什么elementData是使用transient修飾的呢权旷?關(guān)于這個(gè)問題,說說我的看法贯溅。我們看一下ArrayList的定義:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
看到ArrayList實(shí)現(xiàn)了Serializable接口拄氯,這意味著ArrayList是可以被序列化的躲查,用transient修飾elementData意味著我不希望elementData數(shù)組被序列化。這是為什么译柏?因?yàn)樾蛄谢疉rrayList的時(shí)候镣煮,ArrayList里面的elementData未必是滿的,比方說elementData有10的大小鄙麦,但是我只用了其中的3個(gè)典唇,那么是否有必要序列化整個(gè)elementData呢?顯然沒有這個(gè)必要胯府,因此ArrayList中重寫了writeObject方法:
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
每次序列化的時(shí)候調(diào)用這個(gè)方法介衔,先調(diào)用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它盟劫,然后遍歷elementData夜牡,只序列化那些有的元素,這樣:
- 加快了序列化的速度
- 減小了序列化之后的文件大小