1.ArrayList是實作了基於動態數組的資料結構,LinkedList是基於鍊錶的資料結構。
2.對於隨機存取get和set,ArrayList優於LinkedList,因為ArrayList可以隨機定位,而LinkedList要移動指標一步一步的移動到節點處。 (參考數組與鍊錶來思考)
3.對於新增和刪除操作add和remove,LinedList比較佔優勢,只需要對指標進行修改即可,而ArrayList要移動資料來填補被刪除的物件的空間。
ArrayList和LinkedList是兩個集合類,用來儲存一系列的物件參考(references)。例如我們可以用ArrayList來儲存一系列的String或是Integer。那麼ArrayList和LinkedList在效能上有什麼差別呢?什麼時候該用ArrayList什麼時候又該用LinkedList呢?
一.時間複雜度
首先一點關鍵的是,ArrayList的內部實作是基於基礎的物件數組的,因此,它使用get方法存取列表中的任意一個元素時(random-access),它的速度要比LinkedList快。 LinkedList中的get方法是按照順序從清單的一端開始檢查,直到另外一端。對LinkedList而言,存取清單中的某個指定元素沒有更快的方法了。
假設我們有一個很大的列表,它裡面的元素已經排好序了,這個列表可能是ArrayList類型的也可能是LinkedList類型的,現在我們對這個列表來進行二分查找(binary search),比較列表是ArrayList和LinkedList時的查詢速度,看下面的程式:
LinkedList消耗時間:2596
這個結果不是固定的,但基本上ArrayList的時間要明顯小於LinkedList的時間。因此在這種情況下不宜用LinkedList。二分查找法使用的隨機存取(randomaccess)策略,而LinkedList是不支援快速的隨機存取的。對一個LinkedList做隨機存取所消耗的時間與這個list的大小是成比例的。而對應的,在ArrayList中進行隨機存取所消耗的時間是固定的。
這是否顯示ArrayList總是比LinkedList效能好呢?這並不一定,在某些情況下LinkedList的表現要優於ArrayList,有些演算法在LinkedList中實作時效率更高。比方說,利用Collections.reverse方法對清單進行反轉時,其效能就要好些。
看這樣一個例子,如果我們有一個列表,要對其進行大量的插入和刪除操作,在這種情況下LinkedList就是一個較好的選擇。請看如下一個極端的例子,我們重複的在一個清單的開端插入一個元素:
這時我的輸出結果是:ArrayList耗時:2463
LinkedList耗時:15
這和前面一個例子的結果截然相反,當一個元素被加到ArrayList的最開端時,所有已經存在的元素都會後移,這意味著資料移動和複製上的開銷。相反的,將一個元素加到LinkedList的最開端只是簡單的為這個元素分配一個記錄,然後調整兩個連接。在LinkedList的開端增加一個元素的開銷是固定的,而在ArrayList的開端增加一個元素的開銷是與ArrayList的大小成比例的。
二.空間複雜度
在LinkedList中有一個私有的內部類別,定義如下:
private static class Entry {
Object element;
Entry next;
Entry previous;
}
每個Entry物件reference清單中的一個元素,同時還有在LinkedList中它的上一個元素和下一個元素。一個有1000個元素的LinkedList對象將有1000個連結在一起的Entry對象,每個對像都對應到列表中的一個元素。這樣的話,在一個LinkedList結構中將會有一個很大的空間開銷,因為它要儲存這1000個Entity物件的相關資訊。
ArrayList使用一個內建的陣列來儲存元素,這個陣列的起始容量是10.當陣列需要成長時,新的容量按如下公式獲得:新容量=(舊容量*3)/2+1,也就是說每一次容量大概會成長50%。這就意味著,如果你有一個包含大量元素的ArrayList對象,那麼最終將有很大的空間會被浪費掉,這個浪費是由ArrayList的工作方式本身造成的。如果沒有足夠的空間來存放新的元素,則陣列將不得不重新進行分配以便能夠增加新的元素。對數組進行重新分配,將會導致效能急劇下降。如果我們知道一個ArrayList將會有多少個元素,我們可以透過建構方法來指定容量。我們也可以透過trimToSize方法在ArrayList分配完畢之後去掉浪費掉的空間。
三.總結
ArrayList和LinkedList在效能上各有優缺點,都有各自所適用的地方,總的說來可以描述如下:
效能總結:
- | add()操作 | delete()操作 | insert操作 | index取值操作 | iterator取值操作 |
---|---|---|---|---|---|
ArrayList/Vector/Stack | 好 | 差 | 差 | 極優 | 極優 |
LinkedList | 好 | 好 | 好 | 差 | 極優 |
1.對ArrayList和LinkedList而言,在清單最後增加一個元素所花的開銷都是固定的。對ArrayList而言,主要是在內部數組中增加一項,指向所添加的元素,偶爾可能會導致對數組重新進行分配;而對LinkedList而言,這個開銷是統一的,分配一個內部Entry對象。
2.在ArrayList的中間插入或刪除一個元素意味著這個清單中剩餘的元素都會被移動;而在LinkedList的中間插入或刪除一個元素的開銷是固定的。
3. LinkedList不支援高效率的隨機元素存取。
4. ArrayList的空間浪費主要體現在在list列表的結尾預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗相當的空間
可以這樣說:當操作是在一列數據的後面添加數據而不是在前面或中間,並且需要隨機地訪問其中的元素時,使用ArrayList會提供比較好的性能;當你的操作是在一列數據的前面或中間新增或刪除資料,並且按照順序存取其中的元素時,就應該使用LinkedList了。
java中ArrayList 、List區別
List集合
List繼承自Collection介面。 List是一種有序集合,List中的元素可以根據索引(順序號:元素在集合中處於的位置資訊)進行取得/刪除/插入操作。
跟Set集合不同的是,List允許有重複元素。對於滿足e1.equals(e2)條件的e1與e2物件元素,可以同時存在於List集合中。當然,也有List的實作類別不允許重複元素的存在。
同時,List也提供一個listIterator()方法,傳回一個ListIterator介面對象,和Iterator介面相比,ListIterator添加元素的添加,刪除,和設定等方法,還能向前或向後遍歷。
List跟Collection的關係:
java.util.Collection [I]
+--java.util.List [I]
+--java.util.ArrayList [C]
+--java.util.LinkedList [C]
+--java.util.Vector [C]
+--java.util.Stack [C]
List介面的實作類別主要有ArrayList,LinkedList,Vector,Stack等。
父子關係.
List是一個介面,ArrayList繼承與這個介面並實作了它.
用的時候一般都用ArrayList.沒用過List. 可以這麼用:List list = new ArrayList();
Collection介面
Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。有些Collection允許相同的元素而有些不行。有些能排序而有些不行。 Java SDK不提供直接繼承自Collection的類別,JavaSDK提供的類別都是繼承自Collection的「子介面」如List和Set。
所有實作Collection介面的類別都必須提供兩個標準的建構子:無參數的建構子用來建立一個空的Collection,有一個Collection參數的建構子用來建立一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。後一個建構函式允許使用者複製一個Collection。
如何遍歷Collection中的每個元素?不論Collection的實際類型如何,它都支援一個iterator()的方法,該方法傳回一個迭代子,使用該迭代子即可逐一存取Collection中每一個元素。典型的用法如下:
Iterator it = collection.iterator(); // 得到一個迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一個元素
}
由Collection介面派生的兩個介面是List和Set。
List介面:
List是有序的Collection,使用此介面能夠精確的控制每個元素插入的位置。使用者能夠使用索引(元素在List中的位置,類似於陣列下標)來存取List中的元素,這類似於Java的陣列。
和下面要提到的Set不同,List允許有相同的元素。
除了具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator接口,和標準的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設定元素,還能向前或向後遍歷。
實作List介面的常用類別有LinkedList,ArrayList,Vector和Stack。
LinkedList類
LinkedList實作了List接口,允許null元素。另外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆疊(stack),佇列(queue)或雙向佇列(deque)。
注意LinkedList沒有同步方法。如果多個執行緒同時存取一個List,則必須自行實作存取同步。一種解決方法是在建立List時建構一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList類別
ArrayList實作了可變大小的陣列。它允許所有元素,包括null。 ArrayList沒有同步。
size,isEmpty,get,set方法運行時間為常數。但是add方法開銷為分攤的常數,加入n個元素需要O(n)的時間。其他的方法運行時間為線性。
每個ArrayList實例都有一個容量(Capacity),即用於儲存元素的陣列的大小。這個容量可隨著不斷添加新元素而自動增加,但是成長演算法並沒有定義。當需要插入大量元素時,在插入前可以呼叫ensureCapacity方法來增加ArrayList的容量以提高插入效率。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
總結如果涉及堆疊,隊列等操作,應該考慮用List,對於需要快速插入,刪除元素,應該使用LinkedList,如果需要快速隨機存取元素,應該使用ArrayList。
盡量回傳介面而非實際的類型,例如回傳List而非ArrayList,這樣如果以後需要將ArrayList換成LinkedList時,客戶端程式碼就不用改變。這就是針對抽象程式。