ArrayList
類圖
總結
ArrayList
的默認容量是10/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
不同於
Vector
,ArrayList
是線程不安全的,可以使用併發容器CopyOnWirteArrayList
代替ArrayList
底層是使用對象數組實現的,繼承了AbstractList
,實現了Serializable
、Cloneable
、RadndomAccess
接口transient Object[] elementData; // non-private to simplify nested class access
- 使用
writeObject
進行序列化 - 支持隨機訪問
clone()
方法實現的是淺複製
writeObject()
方法中是先序列化當前元素的個數,再序列化每個元素。因此在readObject()
方法中需要先從流中反序列化元素個數,在反序列化每個元素。在這個序列化過程中,是不允許插入、刪除元素和擴容、縮小容量等操作,否則拋出ConcurrentModificationException
異常private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; // 記錄當前操作次數 s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { // 如果序列化完成後,操作次數發生改變 throw new ConcurrentModificationException(); } }
當
ArrayList
容量不足時,會進行容量的擴充,新容量是舊容量的1.5倍:int newCapacity = oldCapacity + (oldCapacity >> 1);
但是,如果新容量不足最小的容量(如默認容量10),則以最小容量爲準
如果使用默認構造函數創建
ArrayList
,則此時的elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA
是一個長度爲0的空數組,也就是說,在第一次add()
方式時,會先進行擴容操作。這是一種懶惰申請數組空間的方式,防止ArrayList
創建以後,卻不使用,從而浪費空間而
trimToSize()
方法正好相反,是將數組長度消減到實際元素個數。如果此時size == 0
則直接將DEFAULTCAPACITY_EMPTY_ELEMENTDATA
賦值給elementData
,從而避免使用Arrays.copyOf()
來複制數組減少消耗需要注意
clear()
方法,僅僅是將數組中的元素全部置爲null
,然後修改size = 0
,但是並不修改數組的長度可以使用
Itr
/ListItr
/get()
方法來遍歷數組,僅僅是隨機訪問的話,get()
方法更爲高效,因爲get()
方法是直接通過下標去訪問元素,而迭代器需要做更多的操作在使用
ArrayList
時,如果提前預知了元素的個數,則最好在構建時生成好預計容量的ArrayList
,否則在動態增長過程中,數組的會進行多次複製元素的操作,這是十分影響效率的ArrayList
是有長度限制的,理論最長爲Interger.MAX_VALUE
LinkedList
類圖
總結
LinkedList
底層使用雙向鏈表實現,所以更適用於順序訪問而不是隨機訪問LiknedList
由於使用鏈表實現,理論上是沒有長度限制的,但是size
則可能會出現溢出的情況LinkedList
繼承至AbstractSequentialList
,而不是直接繼承AbstractList
AbstractSequentialList
提供了對List
接口的基本實現
public E get(int index)
public E set(int index, E element)
public void add(int index, E element)
public E remove(int index)
public boolean addAll(int index, Collection<? extends E> c)
public Iterator<E> iterator()
AbstractSequentialList
實現的方法均是(除了iterator()
)圍繞着抽象方法public abstract ListIterator<E> listIterator(int index);
來進行的,所以說在這個基本實現中,全部是依靠ListIterator
來對底層鏈表進行操作- 注意,如果要實現隨機訪問數組的話最好是直接繼承
AbstractList
,而不是去繼承AbstractSequentialList
LinkedList
實現了List
接口,最基本的可以作爲列表使用,同時實現了Deque
接口,也就是說可以作爲雙端隊列使用,再加上Deque
接口繼承自Queue
接口,那麼也可以作爲隊列使用基於
LinkedList
的實現,也可以被用做(鏈)棧使用addFirst()
—push()
removeFirst()
—pop()
getFirst()
—peek()
LinkedList
同時也提供了隊列的基本操作,需要注意的是,LinkedList
實現的是無界隊列public boolean add(E e)
public boolean offer(E e)
public E remove()
public E poll()
public E element()
public E peek()
LinkedList
同樣是線程不安全的,可以使用Collections.synchronizedList()
來獲取線程安全的LinkedList
。如果只是使用隊列,那麼可以使用ConcurrentLinkedQueue
來代替
ArrayList和LinkedList對比
相同點
- 都是
List
接口的具體實現 - 兩者都是線程不安全的
- 均使用了
modCount
來記錄操作的次數(例如修改數組的結構或內容),在一些方法執行過程中(包括迭代器),不允許其他的操作時,會通過modCount
的變化來拋出ConcurrentModificationException
異常
不同點
底層實現不同:
ArrayList
使用的數組,LinkedList
使用的是雙向鏈表ArrayList
的額外空間消耗是在於通常會在數組的末尾預留一定的空間,LinkedList
則是需要保存相關聯結點的指針ArrayList
實際上有長度限制,而LinkedList
沒有ArrayList
更適用於隨機訪問在插入/刪除元素時,
ArrayList
相比LinkedList
會多付出移動元素以及數組擴容的代價,但是LinkedList
同樣也會付出額外空間的代價,同時在指定下標進行元素的操作時,LinkedList
會付出更多的代價(進行元素的查找)在數據量不大的情況,
ArrayList
和LinkedList
在操作元素方面效率差不多。但是數據量變大,如果需要在列表的前面操作元素或者伴隨着頻繁/大量的元素刪除,那麼使用LinkedList
可能會更好(減少移動元素的情況),但是其他情況下,考慮訪問元素的速度(特別是已知元素上限時),ArrayList
可能就是更優的選擇