java jdk1.7版本的ArrayList原理解析

上一篇文章中我們瞭解了LinkedList集合的底層實現原理,這一篇將研究下ArrayList的實現原理。
同樣的通過源碼解釋,我們可以得到以下信息:
(1)、ArrayList是一個長度可變的數組集合,創建ArrayList實例時沒有指定長度,當添加數據,而且數據個數小於10時,那麼默認將會創建一個容量爲10的數組存儲數據;當數組不夠長時,將會默認增長原來容量的一半。如果要自定義ArrayList的容量,可以通過調用ensureCapacity(int size)方法指定容量,也可以在創建ArrayList時指定容量,ArrayList最大的容量不能超出Integer.MAX_VALUE(2147483647)否則會拋出OutOfMemoryError異常。
(2)、不同的方法訪問ArrayList的時間複雜度不一樣。size, isEmpty, get, set,iterator,listIterator的時間複雜度是固定的,add、remove方法是不固定的,因爲有可能需要移動元素。
(3)、ArrayList不是線程安全的,所以在多線程的環境下使用ArrayList需要注意ArrayList類型變量的線程同步問題。當然,有一種方式可以創建一個線程安全的ArrayList:
List list = Collections.synchronizedList(new ArrayList(…));
(4)、ArrayList的迭代器 iterator 和 listIterator 方法返回的迭代器是快速失敗 的。所謂快速失敗,意思就是如果在迭代器已經創建了的情況下,任何時刻對ArrayList結構的修改,迭代器將會拋出一個ConcurrentModificationException異常。如下面代碼中迭代器已經生成,但是還往ArrayList添加數據,那麼此後的迭代過程是會拋出ConcurrentModificationException異常的:
這裏寫圖片描述

ArrayList底層是基於數組來保存數據的,這個數組就保存着ArrayList的真實數據。內部定義了這個數組:
這裏寫圖片描述
沒錯!elementData就是ArrayList真正保存數據的數組。

接下來我們先了解下ArrayList的容量增長策略,先上源碼:
這裏寫圖片描述

這裏寫圖片描述
對ArrayList進行自增長的入口是ensureCapacityInternal(int minCapacity)方法,這裏說明一下實現自增長過程中有幾個會使用到的常量:
(1)、elementData:這個變量在前面已經說過,是存放ArrayList真實數據的數組;
(2)、EMPTY_ELEMENTDATA:這是一個容量爲0的空數組,這個常量是不會存儲真實數據的,即它永遠都是空的。 源碼是這樣定義的:
這裏寫圖片描述
(3)、DEFAULT_CAPACITY:ArrayList的默認容量,如果ArrayList沒有指定容量,並且需要保存數據,但是數據的真實個數不夠DEFAULT_CAPACITY大小的情況下,將會創建一個DEFAULT_CAPACITY大小的數組(即elementData)來存放添加的數據;
(4)、MAX_ARRAY_SIZE:Integer的最大值,但是源碼中實際的長度會比Integer的最大值小8,因爲java中是這樣定義的:
這裏寫圖片描述

對照這些常量就很好理解上面ArrayList容量實現自增長的原理:這裏總結下:
(1)、如果當前ArrayList的數據爲空,並且添加的數據個數小於10,那麼java將會分配一個長度爲10的數組來存放數據;
(2)、如果當前ArrayList的數據不爲空,並且數組的大小可以容納新增的數據,那麼將不進行自增長操作;
(3)、如果當前ArrayList的數據不爲空,並且數組的大小不可以容納新增的數據,那麼將進行自增長操作,並且是這樣增長:
如果(size+size/2)>size+新增數據的個數,那麼將ArrayList的容量增長到(size+size/2)大小;
如果size+size/2)小於size+新增數據的個數,那麼將ArrayList的容量增長到size+新增數據的個數大小;
如果size+新增數據的個數>MAX_ARRAY_SIZE,那麼將ArrayList的容量增長到Integer.MAX_VALUE大小,否則將ArrayList的容量增長到MAX_ARRAY_SIZE;
如果size+新增數據的個數>Integer.MAX_VALUE,則拋出OutOfMemoryError異常。

這裏需要注意一下,當ArrayList容量擴充完成之後會調用這樣一句代碼:
elementData = Arrays.copyOf(elementData, newCapacity); 這句代碼的意思就是按照新的容量大小來創建一個新的數據,並且把原來數據的數據拷貝過來。不懂 Arrays.copyOf()看看便明白。

知道了ArrayList是基於數組來實現的,下面將看下具體的實現:
1、add(E e):該方法添加一個數據到ArrayList鏈表最後的位置,jdk中的源碼:
這裏寫圖片描述
新增一個數據的源碼非常簡單,如果不看ensureCapacityInternal(size + 1);這一句代碼,實際上就是往數組的size索引這個位置放入一個數據。size是ArrayList的大小。注意,這裏的size不是數組的大小,而是數組裏真正存在數據的大小,也即是ArrayList的大小。比如ArrayList的size是10,但是數據的長度有可能是100,只有前10個位置存放了真正的數據。這裏需要注意!!!

2、add(int index, E element):在指定的index位置添加一個數據,index之後的數據將依次往後移:
這裏寫圖片描述
同樣的,這裏也是直接在數據的index位置放入一個數據,但是在這之前,判斷了傳入的index是否有越界;同時也判斷了ArrayList是否需要擴充容量,並且通過數據拷貝的方式把index後的數據往後移。其實數據的移動都是通過調用System.arraycopy()進行移動的。

3、remove(int index):remove方法移除元素之後,index後面的數據是往前移動,同樣也是通過System.arraycopy()方法實現:
這裏寫圖片描述

4、lastIndexOf(Object o):lastIndexOf直接遍歷數組,但是是從後往前遍歷,因爲是尋找最後一個要查找的對象:
這裏寫圖片描述

ArrayList底層實現原理是通過數組實現,因此其優點是隨機訪問效率比較高,但是隨機插入和刪除元素比較慢,因爲要對其它元素進行移動。

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