小學生也能看懂的ArrayList底層原理

閱讀指引:本文包含源碼,如果不想閱讀源碼,建議跳過 “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 的元素並刪除。

結束語:每天進步一點點~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章