概述(JDK1.8.0_162)
ArrayList基於數組實現,是一個動態數組,其容量能自動增長,類似於C語言中的動態申請內存,動態增長內存。
每個ArrayList實例都有一個容量,該容量是指用來存儲列表元素的數組的大小。它總是至少等於列表的大小。隨着向ArrayList中不斷添加元素,其容量也自動增長。自動增長會帶來數據向新數組的重新拷貝,因此,如果可預知數據量的多少,可在構造ArrayList時指定其容量。在添加大量元素前,應用程序也可以使用ensureCapacity操作來增加ArrayList實例的容量,這可以減少遞增式再分配的數量。 注意,此實現不是同步的。如果多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那麼它必須保持外部同步。
繼承關係
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8683452581122892189L;
}
- 實現了RandomAccess接口,支持快速隨機訪問,實際上就是通過下標序號進行快速訪問
- 實現了Cloneable接口,能被克隆
- 實現了Serializable接口,支持序列化,能夠通過序列化傳輸
主要成員變量
/**
* 默認初始化容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用於空實例的共享空數組實例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 用於默認大小的空實例的共享空數組實例
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存儲ArrayList元素的數組緩衝區
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* ArrayList包含的元素數量
*
* @serial
*/
private int size;
transient
Java的serialization提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數據成員,我們不想用serialization機制來保存它。爲了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。即:被標記爲transient的屬性在對象被序列化時不會被保存。
構造函數
/**
* 初始化時指定容量大小
*/
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);
}
}
/**
* 無參構造函數,默認容量爲10
*/
public ArrayL ist() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 初始化一個包含指定Collection的ArrayList
*/
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 E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
/**
* 將指定的元素添加到此列表的尾部
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 將指定的元素插入此列表中的指定位置,如果當前位置有元素,則向右移動當前位於該位置的元素以及所有後續元素(將其索引加1)
*/
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++;
}
/**
* 按照指定collection的迭代器所返回的元素順序,將該collection中的所有元素添加到此列表的尾部
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
/**
* 從指定的位置開始,將指定collection中的所有元素插入到此列表中
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
元素讀取
/**
* 返回列表中指定位置上的元素
*/
public E get(int index) {
rangeCheck(index);
return elementData(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;
}
/**
* 移除此列表中首次出現的指定元素
*/
public boolean remove(Object o) {
// ArrayList允許存放null值
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;
}
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
}
/**
* 移除列表中所有元素
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
數組擴容
/**
* 擴容此ArrayList實例的容量,確保至少包含minCapacity指定的元素數
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 要分配的最大數組大小,超過此大小可能會導致OutOfMemoryError:請求的數組大小超過VM限制
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
從上述代碼中可以看出,數組進行擴容時,會將老數組中的元素重新拷貝一份到新的數組中,每次數組容量的增長大約是其原容量的1.5倍。這種操作的代價是很高的,因此在實際使用時,我們應該儘量避免數組容量的擴張。當我們可預知要保存的元素的多少時,要在構造ArrayList實例時,就指定其容量,以避免數組擴容的發生。或者根據實際需求,通過調用ensureCapacity方法來手動增加ArrayList實例的容量。
Object oldData[] = elementData;//爲什麼要用到oldData[]
乍一看來後面並沒有用到關於oldData, 這句話顯得多此一舉!但是這是一個牽涉到內存管理的類, 所以要了解內部的問題。 而且爲什麼這一句還在if的內部,這跟elementData = Arrays.copyOf(elementData, newCapacity); 這句是有關係的,下面這句Arrays.copyOf的實現時新創建了newCapacity大小的內存,然後把老的elementData放入。好像也沒有用到oldData,有什麼問題呢。問題就在於舊的內存的引用是elementData, elementData指向了新的內存塊,如果有一個局部變量oldData變量引用舊的內存塊的話,在copy的過程中就會比較安全,因爲這樣證明這塊老的內存依然有引用,分配內存的時候就不會被侵佔掉,然後copy完成後這個局部變量的生命期也過去了,然後釋放纔是安全的。不然在copy的的時候萬一新的內存或其他線程的分配內存侵佔了這塊老的內存,而copy還沒有結束,這將是個嚴重的事情。
對象數組
/**
* 返回這個集合的對象數組
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/**
* 返回這個集合的對象數組。
* 如果傳入數組的長度小於size,返回一個新的數組,大小爲size,類型與傳入數組相同。
* 如果傳入數組的長度等於size,則將elementData複製到傳入數組並返回傳入的數組。
* 如果傳入數組的長度大於size,除了複製elementData外,還將返回數組的第size個元素置爲null。
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Fail-Fast機制
ArrayList也採用了快速失敗的機制,通過記錄modCount參數來實現。在面對併發修改的時候,迭代器很快就會完全失敗,而不是冒着在將來某個不確定時間發生任意不確定行爲的風險。