public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
}
複製代碼
首先能夠看到ArrayList
繼承了AbstractList
並實現了List
,也就是說ArrayList
是一個數組隊列,擁有了基本的增刪改查,遍歷的操做。java
同時實現了數組
Cloneable
接口,便可以被clone
Serializable
接口,意味着能夠被序列化RandomAccess
接口,RandomAccess
接口是一個空的接口,和Serializable
接口同樣,也是做爲一個標識,便可以快速訪問,對於ArrayList
來講,就是能夠經過下標來訪問元素。而LinkedList
就沒有實現這*個接口。根據官方註解知道,DEFAULT_CAPACITY
是數組總空間大小,而size
是數組的當前的容量的大小。舉個例子來講,一個能夠裝1L水的杯子,那麼DEFAULT_CAPACITY
就是1L,咱們如今往杯子裏倒入了0.5L水,那麼size
就是0.5L。固然在這,DEFAULT_CAPACITY
的默認值是10,當size
>10的時候,會進行擴容。安全
剛纔咱們講到ArrayList
內部其實就是用了一個Object[]
來進行維護數據,那既然咱們已經實現了Serializable
接口,那爲撒還要用transient
來修飾elementData
呢?來看看序列化/反序列化的代碼dom
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// like clone(), allocate array based upon size not capacity
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
Object[] elements = new Object[size];
// Read in all elements in the proper order.
for (int i = 0; i < size; i++) {
elements[i] = s.readObject();
}
elementData = elements;
} else if (size == 0) {
elementData = EMPTY_ELEMENTDATA;
} else {
throw new java.io.InvalidObjectException("Invalid size: " + size);
}
}
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 behavioral 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();
}
}
複製代碼
ArrayList
在序列化的時候會調用writeObject
方法,將數組的size
和elementData
寫入ObjectOutputStream
,函數
在反序列化時調用readObject
,從ObjectInputStream
獲取size
和elementData
,再恢復到elementData
.工具
這樣就能夠很好的節省空間和時間。由於elementData
整個的大小是CAPACITY
,通常狀況下都會預留一些容量,咱們真正須要序列化/反序列化的只是當前存入的數據。post
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
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);
}
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
複製代碼
第一個空參構造函數,會默認將DEFAULTCAPACITY_EMPTY_ELEMENTDATA
的引用傳入elementData
。在第一次添加元素的時候會擴容一個容量爲10的數組。this
第二個指定初始化的 capacity
來建立 elementData
, 通常推薦使用這種方式來建立ArrayList
,減小擴容帶來的內存開銷。spa
第三個則是傳入一個Collection
來建立elementData
線程
添加方法有三個重載方法,最終都會調用下面這個add
方法
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 每次擴容1.5倍
//若是擴容後的capcity的大小等於或小於mincapacity,且若是是使用空參構造器初始化的,那麼就返回Math.max(DEFAULT_CAPACITY, minCapacity),不然返回 minCapacity(if minCapacity > 0)
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 使用空參構造函數elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
複製代碼
在第一個方法中,e
是指當前add
的元素,s
指當前arrList
的size。能夠發現,當size
的大小和elementData
的長度相等時,纔會進行擴容,即調用grow
,從newCapacity
方法中能夠發現,每次擴容會是當前size
(即 elementData.length
)的1.5倍。
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
複製代碼
在指定下標條件元素的步驟
index
,若是index
小於0,或者大於size
,那麼會拋出IndexOutOfBoundsException
異常modCount++
,後續會介紹用處arrayList
的size
和當前elementData
的長度是否相等,若是至關那麼先進行擴容System.arraycopy
進行復制,把index
這個下標空出來public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
複製代碼
對於remove(int index)
源碼就比較簡單了,惟一須要注意的就是在fastRemove
中的判斷,若是須要刪除的是數組的最後一個元素,那麼直接將最後一個元素設置爲null
,不然進行arraycopy
,將index
的值覆蓋掉。
前面咱們常常看到modCount++
的操做,那麼爲何要加這個呢?其實當每次對數組進行操做(修改)的時候,都會進行modCount++
,這樣作是爲了記錄修改次數。
咱們知道 ArrayList
不是線程安全的,所以若是在使用迭代器的過程當中若是有其餘線程修改(新增/刪除)了arrayList
的數據(或當前線程在遍歷的過程當中對數據進行修改),那麼將拋出ConcurrentModificationException
,這就是所謂fail-fast策略。
在每次進行遍歷的時候,會先將modCount
賦值給expectedCount
,在迭代過程當中,進行判斷,若是它們不相等,則說明arrayList
的數據已經被修改,拋出異常。
Arrays.copyOf(T[], int length)
方法是 Arrays
工具類中用來進行任意類型數組賦值,並使數組具備指定長度的方法,ArrayList
中用這個方法來實現 elementData
數組的元素移動。但實際上 Arrays.copyOf
方法最終調用的是 System.arraycopy(U[], int srcPos, T[], desPos, int length)
方法,這個方法是一個native
方法
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
複製代碼