Java集合大體能夠分爲Set、List、Queue和Map四種體系,其中Set表明無序、不可重複的集合;List表明有序、重複的集合;而Map則表明具備映射關係的集合,Java 5 又增長了Queue體系集合,表明一種隊列集合實現。node
Collection家族:數組
List集合安全
List集合是有序、可重複的集合數據結構
本篇文章將集中介紹List集合的兩個重要子類ArrayList(而且同時對比Vector)和LinkedList。框架
1.List集合中,判斷元素是否相等須要集合元素類重寫equals()方法。函數
2.List集合中,咱們可使用ListIterator來提供更加厲害的表裏操做,ListIterator相比Iterator,能夠對集合元素在遍歷期間進行修改、插入、刪除操做,而且還能夠向前遍歷。這些在Iterator中是沒法作到的,其緣由是由於List的底層採用數組結構實現,用數組意味着就有標記來記錄各個元素的位置,所以針對List集合能夠經過ListIterator在遍歷期間進行集合元素的修改操做。性能
3.ArrayList與Vector的相同點在於底層都爲數組實現,而且的默認初始長度爲10。不一樣地方在於,進行擴容時,ArrayList每次擴容增長當前容量的50%,Vector則增長一倍。ArrayList爲線程不安全,Vector爲線程安全。ArrayList遍歷方式有Iterator、ListIterator、for循環、加強for循環,Vector遍歷方式有Iterator、ListIterator、for循環、加強for循環、枚舉Enumeration。測試
咱們分別看下Vector與ArrayList的初始化構造函數:spa
再看下Vector與ArrayList的add方法,觀察其擴容策略:線程
Vector
Vector進行擴容操做的邏輯爲
若是此向量的當前容量小於minCapacity,則經過將其內部數組替換爲一個較大的數組倆增長其容量。
新數據數組的大小姜維原來的大小 + capacityIncrement,
除非 capacityIncrement 的值小於等於零,在後一種狀況下,新的容量將爲原來容量的兩倍,不過,若是此大小仍然小於 minCapacity,則新容量將爲 minCapacity。
capacityIncrement爲容量增量,就是每次Vector進行擴容的長度,在Vector進行初始化的時候能夠指定capacityIncrement,不然默認爲0。也就是擴容一倍。
ArrayList
ArrayList的擴容邏輯除了標紅處以外,其他和Vector相同。
4.若是開始就知道ArrayList或Vector集合須要保存多少個元素,則能夠在建立它們時就指定initalCapacity初始長度的大小,這樣能夠提升性能。
此外,ArrayList還提供了兩個額外的方法來調整其容量大小:
void ensureCapacity(int minCapacity) //若有必要,增長此 ArrayList 實例的容量,以確保它至少可以容納最小容量參數所指定的元素數。
void trimToSize() //將此 ArrayList 實例的容量調整爲列表的當前大小。
5.Stack是Vector的子類,用於模擬「棧」這種數據結構,「棧」一般是指「後進先出」(LIFO)的容器。最後「push」進棧的元素,將被最早「pop」出棧。Stack與Vector同樣,是線程安全的,性能較差,儘可能少用Stack類。若是要實現棧」這種數據結構,能夠考慮使用LinkedList。
6.LinkedList類是List接口的實現類——這意味着它是一個List集合,能夠根據索引來隨機訪問集合中的元素。除此以外,LinkedList還實現了Deque接口(繼承了Queue接口的雙端隊列),能夠被看成成雙端隊列來使用,所以既能夠被當成「棧"來使用,也能夠當成隊列來使用。
LinkedList的實現機制與ArrayList徹底不一樣。ArrayList內部是以數組的形式來保存集合中的元素的,所以隨機訪問集合元素時有較好的性能;而LinkedList內部以鏈表的形式來保存集合中的元素,所以隨機訪問集合元素時性能較差,但在插入、刪除元素時性能比較出色。
7.LinkedList調用默認構造函數,建立一個鏈表。因爲維護了一個表頭,表尾的Node對象的變量。能夠進行後續的添加元素到鏈表中的操做,以及其餘刪除,插入等操做。也所以實現了雙向隊列的功能,便可向表頭加入元素,也能夠向表尾加入元素。
下面來了解Node類的具體狀況
由此能夠具體瞭解鏈表是如何串聯起來而且每一個節點包含了傳入集合的元素。
下面以增長操做,具體瞭解LinkedList的工做原理。
調用linkLast(e);方法,默認向表尾節點加入新的元素
更新表尾節點,創建鏈接。其餘操做相似,維護了整個鏈表。
下面具體來看,如何將「雙向鏈表和索引值聯繫起來的」?而且爲何說LinkedList經過下標訪問效率低?
調用了node(index)方法返回了一個Node對象,其中node(index)方法具體以下
首先會比較「index」和「雙向鏈表長度的1/2」;若前者小,則從鏈表頭開始日後查找,直到index位置;不然,從鏈表末尾開始先前查找,直到index位置。這就是「雙線鏈表和索引值聯繫起來」的方法。
到此咱們便會明白,LinkedList在插入、刪除元素時性能比較出色,隨機訪問集合元素時性能較差。
8.LinkedList遍歷方式
LinkedList支持多種遍歷方式。
.經過迭代器遍歷LinkedList
.經過快速隨機訪問遍歷LinkedList(低效)
.經過for循環遍歷LinkedList
.經過pollFirst()遍歷LinkedList
.經過pollLast()遍歷LinkedList
.經過removeFirst()遍歷LinkedList
.經過removeLast()遍歷LinkedList
實現都比較簡單,就不貼代碼了。
其中採用逐個遍歷的方式,效率比較高。採用隨機訪問的方式去遍歷LinkedList的方式效率最低。
LinkedList也是非線程安全的。
9.ArrayList與LinkedList性能對比
ArrayList 是一個數組隊列,至關於動態數組。它由數組實現,隨機訪問效率高,隨機插入、隨機刪除效率低。ArrayList應使用隨機訪問(即,經過索引序號訪問)遍歷集合元素。
LinkedList 是一個雙向鏈表。它也能夠被看成堆棧、隊列或雙端隊列進行操做。LinkedList隨機訪問效率低,但隨機插入、隨機刪除效率高。LinkedList應使用採用逐個遍歷的方式遍歷集合元素。
若是涉及到「動態數組」、「棧」、「隊列」、「鏈表」等結構,應該考慮用List,具體的選擇哪一個List,根據下面的標準來取捨。
(01) 對於須要快速插入,刪除元素,應該使用LinkedList。
(02) 對於須要快速隨機訪問元素,應該使用ArrayList。
(03) 針對迭代的效率問題
LinkedList:高級for循環與Iterator迭代(效率類似)>下標訪問迭代。
ArrayList:下標訪問迭代>高級for循環與Iterator迭代(效率類似)。
測試數據以下: 基於JDK1.8
LinkedList,ArrayList分別經過上面三種方式,遍歷10w次String對象的效率
遍歷100w次
遍歷300w次
Iterator遍歷會被JVM編譯爲
而高級for循環會被編譯爲
(04) 數組和鏈表的遍歷效率問題(後續討論。。。)