陌兮大魔王帶你深入 學習ArrayList(一)

前引

ArrayList的重要性就不言而喻了,在此鄙人就不多說了。本文適合新手閱讀,如果是大神來訪,還望能夠幫忙發現小子知識不足之處,不甚感激。

ArrayList是什麼?

ArrayList是什麼?不就是容器嗎,裝個東西而已!如果你是這樣回答的,那問:它底層到底是什麼?你又如何作答?其實這時候,我們就因該深入其源碼來學習了。一看到源碼啊,許多新手肯定都是拒絕的。不外乎兩種原因:1、代碼看不懂,2、註釋比代碼更看不懂。所以,鄙人希望,我能給大家獻點薄力,爲大家的知識體系添磚蓋瓦。

ArrayList(JDK1.6)源碼:

其實很多源碼都不難.而ArrayList的源碼同樣如此。現在就讓我們走進ArrayList的源碼世界(篇幅有限,挑一些常用的來說):
進入ArrayList的源碼中,首先看到的是這樣一段代碼
private transient Object[] elementData;
private int size;
protected transient int modCount = 0;(來之父類AbstractList,ArrayList不存在)
這個elementData就是用來存放數據的object數組。size是數組的大長度,代表的是ArrayList的所存貯的數據的容量。modCount用來記錄容器被改變的次數(對理解沒什麼用,後文不再解釋這個變量)。
然後我們看到的是ArrayList的構造器
    public ArrayList(int initialCapacity) {
	super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
	this.elementData = new Object[initialCapacity];
    }
    public ArrayList() {
	this(10);
    }
這段代碼是初始化ArrayList用的。從中我們可以看出,實例ArrayList有兩種方法,而我們不一般不帶參數的時候,ArrayList默認的大小是10.即產生一個容量爲10的elementData的object數組。
下一段代碼
    public void ensureCapacity(int minCapacity) {
	modCount++;
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;
    	    if (newCapacity < minCapacity)
		newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
	}
    }
這一段是爲了確保數組可以存放下不停增多的數據,而專門設計的增加數組的長度的方法。
從中 可以看出實現思路是:當需要存放數據到elementData數組中去的時候,會傳入minCapcity(表示該數組應存入的總數據量),如果minCapcity大於現在的數組的長度,就產生一個新的數組,容量爲以前的1.5倍+1,如果仍然小於,就直接將容量設置爲minCapcity。然後再將之前存放在原數組的數據複製到新的數組中去,就實現了整個數組的擴容!
看完前置準備的各種方法之後,可以看操作的方法了:
size():
    public int size() {
	return size;
    }
size就是elementData的長度。

    public boolean isEmpty() {
	return size == 0;
    }
返回size==0的判斷結果
indexOf(Object o):
    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;
    }
傳入一個對象,返回其索引。 可以看出這個方法只是將傳入的對象和elementData中的每個元素進行比較,然後再返回其數組下標號。找不到的話,就返回-1。contains(Object o)也只是調用了這個方法而已。
lastIndexOf(Object o):
    public int lastIndexOf(Object o) {
	if (o == null) {
	    for (int i = size-1; i >= 0; i--)
		if (elementData[i]==null)
		    return i;
	} else {
	    for (int i = size-1; i >= 0; i--)
		if (o.equals(elementData[i]))
		    return i;
	}
	return -1;
    }
可以看見lastIndexOf方法和indexOf是如此的相似,只是將遍歷方向改變了一下就又多了一個方法,哈哈!
get(index):
    public E get(int index) {
	RangeCheck(index);

	return (E) elementData[index];
    }
    private void RangeCheck(int index) {
	if (index >= size)
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);
    }
set(index,object)
    public E set(int index, E element) {
	RangeCheck(index);


	E oldValue = (E) elementData[index];
	elementData[index] = element;
	return oldValue;
    }
可以發現很多操作都只是在操作數組而已!畢竟ArrayList就是數組。
add(o):
    public boolean add(E e) {
	ensureCapacity(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
    }
add方法也是如此,只是此處需要判斷是否要擴容
add(index,object):
    public void add(int index, E element) {
	if (index > size || index < 0)
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);

	ensureCapacity(size+1);  // Increments modCount!!
	System.arraycopy(elementData, index, elementData, index + 1,
			 size - index);
	elementData[index] = element;
	size++;
    }
在某個位置插入一個對象,這段代碼難得也就這就話:System.arraycopy(elementData, index, elementData, index + 1, size - index); 其實這句好的意思是:將elementData從其第index和後面的元素,複製到elementData的第index+1和之後的位置,共複製size-index個元素,這樣就是實現了將第index爲元素騰空,讓外界傳進來的object佔據這個寶座。這也是前面擴容的時候使用的是size+1而不是size的原因(確保數組複製時弄的size+1不會越界)
remove(index):
    public E remove(int index) {
	RangeCheck(index);

	modCount++;
	E oldValue = (E) elementData[index];

	int numMoved = size - index - 1;
	if (numMoved > 0)
	    System.arraycopy(elementData, index+1, elementData, index,
			     numMoved);
	elementData[--size] = null; // Let gc do its work

	return oldValue;
    }
可以發現和add是差不多的,而且還不許要考慮擴容的問題。同樣的remove(object)相比起來,就多了一個判斷該對象的index在哪,然後直接調用這個方法就行了。

總結:

看到這裏,想必大家對ArrayList也算有一個清晰地認知了。同時也明白了源碼並不是很難,而且觀看源碼可以很好地加深我們對知識的理解,理解到底層。這樣的學習會讓我們的知識變得更加牢靠!由於今天實在是太晚了,所以沒有貼上自定義實現的MyArrayList的代碼(還沒有寫哭),所以今天只能告一段落了,若是大家還覺得不過癮,想對容器有進一步的加深的慾望,希望能夠繼續觀看我的博文。以後的文章我會逐一地對LinkedList,Map,Set,Tree等容器進行源碼的解析,還希望大家能夠多多支持,謝謝大家!
如果有什麼問題,還希望能夠一起討論,若是有什麼不足之處,請一定要告訴我,謝謝大笑










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