JAVA集合原始碼學習——ArrayList
ArrayList概述
1)ArrayList是基於長度可動態增長的陣列實現List介面的java集合類
2)ArrayList類內部維護者一個動態可再分配的Object[]陣列,每一個類物件都有一個capacity屬性,表示所封裝的Object[]物件陣列的長度,但往ArrayList新增元素時,該屬性值會自動增加,體現了動態的特點
3)如果要往ArrayList裡一次新增大量元素可以呼叫ensureCapacity方法傳入滿足新增元素的最終容量,減少ArrayList每次自動重分配的次數從而提高效能
4)ArrayList和vector區別是:ArrayList是非同步的即執行緒不安全,當有多執行緒訪問該集合時,程式要手動保證集合的同步;相反vector則是同步,執行緒安全的
5)ArrayList和Collection關係:
ArrayList資料結構
arrayList的資料結構:
arryList底層的資料結構就是陣列,所有對ArrayList的操作都是基於對陣列的操作
ArrayList原始碼分析
1、繼承結構圖
其繼承結構:
ArrayList extends AbstractList
AbstractList extends AbstractCollection
分析:
1)ArrayList為什麼要繼承abstractList而不直接實現list介面?其實採用的就是介面卡的思想,方便ArrayList的程式碼更簡潔
2)ArrayList實現的介面
RandomAccess介面:標記性介面,用於快速存取,即實現了該介面後用for迴圈遍歷比使用迭代器迭代的效率更高
3)Cloneable介面:可以使用Object.Clone()方法
4)Serializable介面:實現序列化介面,可以實現序列化功能儲存
5)類中屬性
1、無參建構函式
Constructs an empty list with an initial capacity of ten. 這裡就說明了預設會給10的大小,所以說一開始arrayList的容量是10.ArrayList中儲存資料的其實就是一個數組,這個陣列就是elementData private transient Object[] elementData; public ArrayList() { super(); //呼叫父類中的無參構造方法,父類中的是個空的構造方法 this.elementData = EMPTY_ELEMENTDATA;//EMPTY_ELEMENTDATA: 是個空的Object[], 將elementData初始化,elementData也是個Object[]型別。 空的Object[]會給預設大小10,等會會解釋什麼時候賦值的。 }
2、有參建構函式
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super(); //父類中空的構造方法
if (initialCapacity < 0) //判斷如果自定義大小的容量小於0,則報下面這個非法資料異常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity]; //將自定義的容量大小當成初始化elementData的大小
}
7)核心方法
1、boolean add(E);//預設直接在末尾加元素
*Appends the specified element to the end of this list.新增一個特定的元素到list的末尾。
*@param e element to be appended to this list
*@return <tt>true</tt> (as specified by {@link Collection#add}
*/
public boolean add(E e) {
//確定內部容量是否夠了,size是陣列中資料的個數,因為要新增一個元素,所以size+1,先判斷size+1的這個個數陣列能否放得下,就在這個方法中去判斷是否陣列.length是否夠用了。
ensureCapacityInternal(size + 1); // Increments modCount!!
//在資料中正確的位置上放上元素e,並且size++
elementData[size++] = e;
return true;
}
分析:
ensureCapacityInternal(xxx);確定內部容量的方法
*
*@param
*/
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {//看,判斷初始化的elementData是不是空的陣列,也就是沒有長度
//因為如果是空的話,minCapacity=size+1;其實就是等於1,空的陣列沒有長度就存放不了,所以就將minCapacity變成10,也就是預設大小,但是帶這裡,還沒有真正的初始化這個elementData的大小。
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//確認實際的容量,上面只是將minCapacity=10,這個方法就是真正的判斷elementData是否夠用
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity(xxx);
*
*@param
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious codeminCapacity如果大於了實際elementData的長度,那麼就說
elementData陣列的長度不夠用,不夠用那麼就要增加elementData的length。
minCapacity到底是什麼呢,這裡給你們分析一下
第一種情況:由於elementData初始化時是空的陣列,那麼第一次add的時候,minCapacity=size+1;也就minCapacity=1,在上一個方法(確定內部容量ensureCapacityInternal)就會判斷出是空的陣列,就會給將minCapacity=10,到這一步為止,還沒有改變elementData的大小
第二種情況:elementData不是空的陣列了,那麼在add的時候,minCapacity=size+1;也就是minCapacity代表著elementData中增加之後的實際資料個數,拿著它判斷elementData的length是否夠用,如果lengt不夠用,那麼肯定要擴大容量,不然增加的這個元素就會溢位。 if (minCapacity - elementData.length > 0)
//arrayList能自動擴充套件大小的關鍵方法就在這裡了
grow(minCapacity);
}
grow(minCapacity)
*
*@param
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //將擴充前的elementData大小給oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity就是1.5倍的oldCapacity
if (newCapacity - minCapacity < 0)//這句話就是適應於elementData就空陣列的時候,length=0,那麼oldCapacity=0,newCapacity=0,所以這個判斷成立,在這裡就是真正的初始化elementData的大小了,就是為10.前面的工作都是準備工作。
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//如果newCapacity超過了最大的容量限制,就呼叫hugeCapacity,也就是將能給的最大值給newCapacity
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//新的容量大小已經確定好了,就copy陣列,改變容量大小咯。
elementData = Arrays.copyOf(elementData, newCapacity);
}
hugeCapacity()
*@param
*/
//這個就是上面用到的方法,很簡單,就是用來賦最大值。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();//如果minCapacity都大於MAX_ARRAY_SIZE,那麼Integer.MAX_VALUE返回,反之將MAX_ARRAY_SIZE返回。因為maxCapacity是三倍的minCapacity,可能擴充的太大了,就用minCapacity來判斷了。//Integer.MAX_VALUE:2147483647MAX_ARRAY_SIZE:2147483639
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2、void add(int , E);//在特定位置新增元素
*
*/
public void add(int index, E element) {
rangeCheckForAdd(index);//檢查index也就是插入的位置是否合理。//跟上面的分析一樣,具體看上面
ensureCapacityInternal(size + 1); // Increments modCount!!
//這個方法就是用來在插入元素之後,要將index之後的元素都往後移一位,
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在目標位置上存放元素
elementData[index] = element;
size++;//size增加1
}
rangeCheckForAdd(index)
System.arraycopy(xxx)就是把elementData在插入位置後的所有元素往後面移一位
小結:
正常情況下會擴容1.5倍,特殊情況下(新擴充套件陣列大小已經達到了最大值)則只取最大值
當我們呼叫add方法時,實際的函式呼叫
3)remove(int);//刪除指定位置上的元素
*@
*/
public E remove(int index) {
rangeCheck(index);//檢查index的合理性
modCount++;//這個作用很多,比如用來檢測快速失敗的一種標誌。
E oldValue = elementData(index);//通過索引直接找到該元素
int numMoved = size - index - 1;//計算要移動的位數。
if (numMoved > 0)
//這個方法也已經解釋過了,就是用來移動元素的。
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//將--size上的位置賦值為null,讓gc(垃圾回收機制)更快的回收它。
elementData[--size] = null; // clear to let GC do its work
//返回刪除的元素。
return oldValue;
}
4)remove(Object);//從這裡可以看ArrayList是可以存放null值
*
*/
//感覺這個不怎麼要分析吧,都看得懂,就是通過元素來刪除該元素,就依次遍歷,如果有這個元素,就將該元素的索引傳給fastRemobe(index),使用這個方法來刪除該元素,
//fastRemove(index)方法的內部跟remove(index)的實現幾乎一樣,這裡最主要是知道arrayList可以儲存null值
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;
}
5)clear();//將elementData中每個元素賦值為null,等待垃圾回收回收
*@return <tt>true</tt> (as specified by {@link Collection#add}
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
6)set()方法
7)indexof方法
*@
// 從首開始查詢數組裡面是否存在指定元素
public int indexOf(Object o) {
if (o == null) { // 查詢的元素為空
for (int i = 0; i < size; i++) // 遍歷陣列,找到第一個為空的元素,返回下標
if (elementData[i]==null)
return i;
} else { // 查詢的元素不為空
for (int i = 0; i < size; i++) // 遍歷陣列,找到第一個和指定元素相等的元素,返回下標
if (o.equals(elementData[i]))
return i;
}
// 沒有找到,返回空
return -1;
}
8)get(index)方法
總結
1)ArrayList可以存放null
2)ArrayList本質是一個elementData陣列
3)ArrayList區別於一般陣列是在於自動擴充套件大小,依賴的方法就是grow()方法
4)ArrayList由於資料結構是陣列因此查詢資料很快但刪除和插入方面效能不高
5)ArrayList實現了RandomAccess,所以遍歷時推薦使用for迴圈