ArrayList原始碼剖析~
包路徑:package java.util;
一、基本屬性
1、private static final long serialVersionUID = 8683452581122892189L;
該屬性是序列化的時候使用的,serialVersionUID是用來表明類的不同版本之間的相容性,若一個序列化類沒有宣告這樣一個static final的常量,JVM會根據各種引數為該類計算一個,對於同一個類,不同的JDK版本可能得出不同的serialVersionUID。
2、private static final int DEFAULT_CAPACITY = 10; 陣列的預設初始容量大小
3、private static final Object[ ] EMPTY_ELEMENTDATA = { }; 陣列的初始化使用
4、private transient Object[ ] elementData; ArrayList的底層資料結構是陣列
transient關鍵字:在採用Java預設的序列化機制的時候,被該關鍵字修飾的屬性不會被序列化。
5、private int size; 集合中儲存元素的個數
6、protected transient int modCount = 0;
在父類AbstractList中定義了該屬性:modCount,是用來記錄AbstractList結構性變化的次數。在ArrayList所有涉及結構性變化的方法中(增加、刪除、修改等)都涉及modCount值++操作。
二、建構函式
1、有參構造如下:傳入的引數是陣列預設的初始化大小,直接對陣列elementData屬性進行初始化
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
2、無參構造如下:elementData陣列為空
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
3、有參構造如下:傳入的引數是一個集合型別的物件
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
三、繼承關係
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
繼承AbstractList類,實現了List<E>等的介面,ArrayList是List介面的一個實現類。
RandomAccess:該介面是一個標誌介面,檢視該介面的原始碼會發現,該介面是一個空的,但是隻要List集合實現這個介面,支援快速隨機訪問,我們即可以通過元素的序號快速獲取元素物件。總的來說,實現了這個介面的List,使用for迴圈獲取資料優於使用迭代器獲取資料,LinkedList沒有實現這個介面,那麼對於它來說,使用迭代器獲取資料優於使用for迴圈獲取資料。
Cloneable:可以進行克隆,
java.io.Serializable:可以進行序列化和反序列化的操作
四、增長方式
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);
}
預設陣列的初始化大小是10,按照原陣列大小的1.5倍方式進行擴容增長。
兩個if語句相當於是給擴容後的大小定了一個上下界。
五、CRUD
1、增加元素的方法(add、addAll)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
傳入的引數是一個:元素
ensureCapacityInternal()方法:首先判斷陣列是否為空,若為空,陣列的容量大小設定為10,確保陣列的容量足夠存放新加入的元素,若不夠,需要進行擴容
將元素插入到elementData陣列中,相應的size+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++;
}
傳入的引數是兩個:元素和想要新增的位置下標
rangeCheckForAdd():該方法是用來檢查傳入的位置下標是否合法,不合法丟擲下標越界的異常
System.arraycopy():
public static native void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
將陣列src從下標srcPos進行拷貝,一直拷貝length個元素到dest陣列中,在dest陣列中從destPos下標開始加入先前的srcPos陣列。
也就是說此處呼叫這個方法是要把插入的位置空出來,儲存插入的元素
addAll()方法同理上述兩個傳入引數不同的add()方法,只是傳入的引數由資料變成了集合型別的物件。
特點:可以儲存重複的資料,也可以儲存null
2、刪除元素的方法(remove、removeAll、removeRange)
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;
}
按下標刪除,首先檢查刪除下標的合法性,然後找出要刪除下標所對應的元素,使用system.arraycopy()方法進行值的挪動拷貝(覆蓋掉要刪除的下標元素),刪除元素意味著size要相應的減少。
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;
}
按元素進行刪除,查詢元素,找到要刪除元素的下標,呼叫fastRemove()方法,傳入刪除下標,進行快速刪除。
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
從一個起始位置到結束位置之間的元素全部刪除,具體就不再解釋,該方法未呼叫別的陌生的方法,
removeAll():該方法同上述的思想,只是傳入的引數是一個集合型別的物件,呼叫了batchRemove方法進行刪除。
特點:陣列的起始元素下標是0,找到第一個出現的元素直接刪除並返回,時間複雜度:O(n)
3、獲取當前元素的方法(get)
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
通過下標進行獲取,首先檢查下標的合法性,通過所以位置直接查詢陣列elementData相對應的元素,時間複雜度:O(1),查詢效率很高。
4、遍歷ArrayList中的物件
public Iterator<E> iterator() {
return new Itr();//返回一個內部類物件
}
iterator()方法是在AbatractList類當中實現了的,該方法返回AbstractList的一個內部類Itr物件。
六、總結
1、ArrayList基於陣列的方式實現,沒有容量的限制(擴容)
2、新增元素可能需要擴容(最好首先預判一下)
3、執行緒不安全
4、內部類:ArrayList有三個內部類
private class Itr implements Iterator<E>
private class ListItr extends Itr implements ListIterator<E>
private class SubList extends AbstractList<E> implements RandomAccess
Itr實現了Iterator介面,重寫了裡面的hasNext()、next()、remove()方法。
ListItr繼承了Itr而且實現了ListIterator介面,重寫了hasPrevious(),nextIndex(),previousIndex(),previous(),set(E e),add(E e)等方法。
SubList繼承了AbstractList,實現了RandomAccess介面,類內部實現了對子序列的增刪改查等方法,但它同時也充分利用了內部類的優點,就是共享ArrayList的全域性變數,例如檢查器變數modCount,陣列elementData等,所以SubList進行的增刪改查操作都是對ArrayList的陣列進行的,並沒有建立新的陣列。