閱讀指引:本文包含源碼,如果不想閱讀源碼,建議跳過 “ArrayList 的主要方法” 中的源碼分析部分,直接看每一部分的小總結。
簡單介紹
ArrayList是 Java 集合框架中比較常用的數據結構,底層基於數組實現,能夠實現容量的動態變化。
我們之所以使用它,與它能夠自由添加、刪除元素有關,使用者不用考慮數組的容量、遍歷等問題,可以說是一個功能強大、使用簡單的數組。
那麼ArrayList裏面到底是怎麼實現的呢?
ArrayList 的成員變量
瞭解一個類,我們需要先了解它有哪些變量,通過這些成員變量我們能夠大致瞭解它的實現原理。
ArrayList 的主要變量:
private static final int DEFAULT_CAPACITY = 10; //數組初始容量
transient Object[] elementData; //存放數組
private int size; //實際元素個數,指已經使用的空間數
除此之外,ArrayList 的父類也有一個重要的變量:
protected transient int modCount = 0; //記錄對 List 操作的次數
看到這裏,我們不着急看具體方法,讓我來猜想一下 ArrayList 主要方法的實現:
add(E e)
:在數組末尾添加元素。若數組爲空,則初始化數組,容量爲10;若數組已滿,則擴容爲原來的1.5倍。get(int index)
:返回指定位置的元素。若index越界,報錯。remove(Object o)
:刪除第一次出現對象爲 o 的元素。若 o 爲 null,查找並刪除值爲 null 的元素;若 o 不爲 null,查找並刪除 o 。size()
:返回ArrayList 中已有元素數量。返回 size。
----------------------------------------------------下面是源碼,慎入----------------------------------------------------------
ArrayList 的主要方法
接下來將從源碼入手,分析 ArrayList 的主要方法是怎麼實現的,可以先深吸一口氣,我也將用比較簡單的話來描述實現的過程。
1.add方法
有多種不同參數的 add() 方法,我們分析一下比較常用的add(E e):
注:代碼中的中文註釋是筆者添加的
public boolean add(E e) {
// 檢查內部容量是否充足,如果不足則擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 將元素存入數組
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 如果初始數組爲默認空數組,minCapacity = 10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 檢查是否需要擴容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 添加新元素後數組長度 大於 數組當前容量
if (minCapacity - elementData.length > 0)
// 數組擴容,擴展爲原來的 1.5 倍
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 相當於 newCapacity = oldCapacity + oldCapacity / 2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
總結:
調用 add(E e) 方法,首先判斷 elementData 是否爲空數組,如果是空數組,初始化 elementData 容量爲 10;如果不是空數組,則檢查 elementData 數組是否已滿,如果已滿則進行擴容,擴展爲原來的 1.5 倍。最後將元素 e 添加到 elementData 數組末尾。
2.get方法
該方法將 返回此列表中指定位置的元素。
public E get(int index) {
// 數組下標越界檢查
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
// 如果下標 大於 數組元素數量
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
// 返回該元素
return (E) elementData[index];
}
總結
調用 get(int index) 方法,首先判斷下標 index 是否越界,越界則拋異常,沒越界則直接返回對應下標的元素。
3.remove方法
該方法 將刪除列表中第一次出現的指定元素。
public boolean remove(Object o) {
// 如果 o 爲空
if (o == null) {
// 遍歷 elementData, 找出 值爲 null 的元素並刪除
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
// 如果 o 不爲空
} else {
// 遍歷 elementData, 找出 值爲 o 的元素並刪除
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
// 刪除指定下標元素
// 讓 index 後的元素整體往前 copy, 然後舊數組的最後一個元素位置賦值爲 null
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
}
總結
調用 remove(Object o)方法,需要先判斷 o 是否爲 null,爲空則遍歷 elementData,找出值爲 null 的元素並刪除;如果不爲空,遍歷 elementData,找出值爲 o 的元素並刪除。
結束語:每天進步一點點~