1. 程式人生 > >java源碼研究--ArrayList實現

java源碼研究--ArrayList實現

() this 實例 常見 如何 定位 所有 true 1.5

java.util.ArrayList是十分常用的容器之一,本文針對其常用方法,對其進行簡單的研究。
ArrayList常見方法如下,主要還是增刪改查:

技術分享圖片

首先,看一下ArrayList中如何保存數據的:

transient Object[] elementData;

所以,所有的數據都是保存在數組裏的。當然,數組都有個大小:

若ArrayList使用無參構造函數實例化:

ArrayList<Integer> arrayList = new ArrayList<Integer>();

那麽,內部數組的大小默認就是10:

1 public ArrayList() {
2 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //空數組 3 } 4 5 //剛實例化完,其實數組為空數組,在往arrayList添加數組時才會擴充容量(默認大小為10),擴充過程繼續往後看。 6 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 7 private static final int DEFAULT_CAPACITY = 10; //默認最小容量為10

下面,研究一下add(E element)方法:

public boolean add(E e) {
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
}

  其中,ensureCapacityInternal函數用來確保數組大小足夠使用(至少為size+1),該函數如下:

 1 private void ensureCapacityInternal(int minCapacity) {
 2     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //若為空數組(實例化完成後),進入此分支
 3         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //minCapacity = Math.max(10, 1);
 4
} 5 6 ensureExplicitCapacity(minCapacity); //ensureExplicitCapacity(10); 7 } 8 9 private void ensureExplicitCapacity(int minCapacity) { 10 modCount++; 11 12 // overflow-conscious code 13 if (minCapacity - elementData.length > 0) //10-0>0 14 grow(minCapacity); //grow(10); 這就是首次實例化之後的擴充容量了; 15 } 16 17 private void grow(int minCapacity) { 18 // overflow-conscious code 19 int oldCapacity = elementData.length; 20 int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity 擴充為1.5倍 21 if (newCapacity - minCapacity < 0) 22 newCapacity = minCapacity; 23 if (newCapacity - MAX_ARRAY_SIZE > 0) 24 newCapacity = hugeCapacity(minCapacity); 25 // minCapacity is usually close to size, so this is a win: 26 elementData = Arrays.copyOf(elementData, newCapacity); //首次插數據,將容量擴充為10;之後容量不夠時,數組容量按照1.5倍擴充。 27 }

可見,在首次插數據時,容量首先將數組容量擴充為10,再執行elementData[size++]=e;
在數組滿了,繼續插數據時,會先將容量擴充為1.5倍(10+10*1/2),再繼續插數據;
當數組又滿了,再繼續擴充為1.5倍(15+15*1/2)......

因此,在明知數據量很大的情況下,初始化時應該指定合適大小的容量,避免arrayList反復擴充容量

例如:若數組總容量為8000,可以這樣實例化:

ArrayList<Integer> arrayList = new ArrayList<Integer>(10000);

帶參構造函數如下:

public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
		this.elementData = new Object[initialCapacity];
	}
	.....
}

  

下面研究一下add(int index, E element)

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++;
}

這個函數是將新元素插入到數組的指定位置(index),從上面的源碼來看,實現過程是先將原先index位置及置換的元素向後移1位,然後再將新元素放在位置index。

  

繼續研究一下remove函數的實現

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;
}

所以,類似的實現原理:將index+1位置及之後的元素先前移動1位,再把最後一個元素清掉。

了解了ArrayList實現原理,才能更好的選擇合適的容器,以及以正確的方式使用,以獲取高效率。

後面將介紹另外一個List接口容器:LinkedList

java源碼研究--ArrayList實現