標籤:
繼續上一篇的容器文章認識容器,泥瓦匠慢慢帶你們走進List的容器解說。今天泥瓦匠想說說 ArrayList 、LinkedList和Vector比較。
一、List回顧
序列(List),有序的Collection,正如它的名字一樣,是一個有序的元素列表。確切的講,列表通常允許滿足 e1.equals(e2) 的元素對 e1 和 e2,並且如果列表本身允許 null 元素的話,通常它們允許多個 null 元素。實現List的有:ArrayList、LinkedList、Vector、Stack等。值得一提的是,Vector在JDK1.1的時候就有了,而List在JDK1.2的時候出現,待會我們會聊到ArrayList和Vector的區別。
二、ArrayList vs. Vector
ArrayList是一個可調整大小的數組實現的序列。隨著元素增加,其大小會動態增加。此類在Iterator或ListIterator迭代中,調用容器自身的remove和add方法進行修改,會拋出ConcurrentModificationException並發修改異常。
注意,此實現不是同步的。如果多個線程同時訪問一個 ArrayList 執行個體,而其中至少一個線程從結構上修改了列表,那麼它必須 保持外部同步。(結構上的修改是指任何添加或刪除一個或多個元素的操作,或者顯式調整底層數組的大小;僅僅設定元素的值不是結構上的修改。)這一般通過對自然封裝該列表的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedList 方法將該列表“封裝”起來。這最好在建立時完成,以防止意外對列表進行不同步的訪問:
List list = Collections.synchronizedList(new ArrayList(...));
下面示範下相關ArrayList例子。
ArrayList基本方法代碼:
@SuppressWarnings({ "rawtypes", "unchecked" }) public static void listMethods() { List a1 = new ArrayList<String>(); a1.add("List01"); a1.add("List03"); a1.add("List04"); System.out.print("原來集合:\n\t"+a1+"\n"); a1.add(1,"List02"); System.out.print("指定角標1插入:\n\t"+a1+"\n"); a1.remove(2); System.out.print("指定角標2刪除:\n\t"+a1+"\n"); System.out.print("指定角標2查詢:\n\t"+a1.get(2)+"\n"); Iterator i1 = a1.iterator(); System.out.println("用迭代器查詢全部元素:"); while (i1.hasNext()) { System.out.print(i1.next()+","); } }
可以從控制台可以看出:
原來集合: [List01, List03, List04]指定角標1插入: [List01, List02, List03, List04]指定角標2刪除: [List01, List02, List04]指定角標2查詢: List04用迭代器查詢全部元素:List01,List02,List04
在上面我們可以根據角標來增加(add)、刪除(remove)、擷取(get)列表裡面元素。ArrayList提供了Iterator迭代器來遍曆序列。值得注意的是,迭代器的就相當於一個指標指向角標,next()方法就相當於指標往後移一位。所以切記,用迭代器中一次迴圈用一次next()。
下面示範下在ConcurrentModificationException的出現,及處理方案。泥瓦匠用Iterator示範這個異常的出現:
@SuppressWarnings({ “unchecked”, “rawtypes” })public static void iteratorTest(){List a1 = new ArrayList<String>();a1.add(“List01″);a1.add(“List02″);a1.add(“List04″);a1.add(“List05″);Iterator i1 = a1.iterator();while (i1.hasNext()){Object obj = i1.next();if (obj.equals(“List02″))a1.add(“List03″);}System.out.print(“集合:\n\t”+a1+”\n”);}
運行,我們可以在控制台看到:
怎麼解決的,先看清楚這個問題。問題描述很清楚,在建立迭代器之後,除非通過迭代器自身的 remove 或 add 方法從結構上對列表進行修改,否則在任何時間以任何方式對列表進行修改,迭代器都會拋出ConcurrentModificationException。
因此我們應該這樣修改代碼,用ListIterator迭代器提供方法,:
@SuppressWarnings({ "unchecked", "rawtypes" }) public static void listIterator() { List a1 = new ArrayList<String>(); a1.add("List01"); a1.add("List"); a1.add("List03"); a1.add("List04"); ListIterator l1 = a1.listIterator(); while (l1.hasNext()) { Object obj = l1.next(); if (obj.equals("List")) { l1.remove(); l1.add("List02"); } } System.out.print("集合:\n\t"+a1+"\n"); }
運行下,我們可以看到:
集合: [List01, List02, List03, List04]
這樣,我們成功解決了這個並發修改異常。把其中‘List’元素刪除,新增了一個‘List02’的元素。
Vector非常類似ArrayList。早在JDK1.1的時候就出現了,以前沒有所謂的List介面,現在此類被改進為實現List介面。但與新的Collection不同的是,Vector是同步的。泥瓦匠想說的是Vector,在像查詢的效能上會比ArrayList開銷大。下面示範下Vector的基本例子:
@SuppressWarnings({ "unchecked", "rawtypes" }) public static void vectorMethods() { Vector v1 = new Vector<String>(); v1.add("Vector001"); v1.add("Vector002"); v1.add("Vector003"); v1.add("Vector004"); v1.add("Vector005"); Enumeration e1 =v1.elements(); while (e1.hasMoreElements()) { Object object = e1.nextElement(); System.out.println(object); } }
從方法上看幾乎沒差別,同樣注意的是:此介面的功能與 Iterator 介面的功能是重複的。此外,Iterator 介面添加了一個可選的移除操作,並使用較短的方法名。新的實現應該優先考慮使用 Iterator 介面而不是 Enumeration 介面。
三、LinkedList及其與ArrayList效能比
LinkedList與ArrayList一樣實現List介面,LinkedList是List介面鏈表的實現。基於鏈表實現的方式使得LinkedList在插入和刪除時更優於ArrayList,而隨機訪問則比ArrayList遜色些。LinkedList實現所有可選的列表操作,並允許所有的元素包括null。除了實現 List 介面外,LinkedList 類還為在列表的開頭及結尾 get、remove 和 insert 元素提供了統一的命名方法。這些操作允許將連結清單用作堆棧、隊列或雙端隊列。
LinkedList和ArrayList的方法時間複雜度總結如所示。
表中,添加add()指添加元素的方法,remove()是指除去(int index)角標。ArrayList具有O(N)的任意指數時間複雜度的添加/刪除,但O(1)的巨集指令清單的末尾。鏈表的O(n)的任意指數時間複雜度的添加/刪除,但O(1)操作端/列表的開始。
泥瓦匠用代碼驗證下這個結論:
public static void testPerBtwnArlAndLkl() { ArrayList<Integer> arrayList = new ArrayList<Integer>(); LinkedList<Integer> linkedList = new LinkedList<Integer>(); // ArrayList add long startTime = System.nanoTime(); long endTime; long duration; for (int i = 0; i < 100000; i++) { arrayList.add(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println("ArrayList add: " + duration); // LinkedList add startTime = System.nanoTime(); for (int i = 0; i < 100000; i++) { linkedList.add(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println("LinkedList add: " + duration); // ArrayList get startTime = System.nanoTime(); for (int i = 0; i < 10000; i++) { arrayList.get(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println("ArrayList get: " + duration); // LinkedList get startTime = System.nanoTime(); for (int i = 0; i < 10000; i++) { linkedList.get(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println("LinkedList get: " + duration); // ArrayList remove startTime = System.nanoTime(); for (int i = 9999; i >=0; i--) { arrayList.remove(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println("ArrayList remove: " + duration); // LinkedList remove startTime = System.nanoTime(); for (int i = 9999; i >=0; i--) { linkedList.remove(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println("LinkedList remove: " + duration); }
控制台輸出如下:
ArrayList add: 16904776LinkedList add: 12015418ArrayList get: 1304593LinkedList get: 108950741ArrayList remove: 787388127LinkedList remove: 128145950
對比下的話,其效能差距很明顯。LinkedList在添加和刪除中效能快,但在擷取中效能差。從複雜度和測試結果,我們應該懂得平時在添加或者刪除操作頻繁的地方,選擇LinkedList時考慮:
1、沒有大量的元素的隨機訪問
2、添加/刪除操作
自然我下面用LinedList實現一個資料結構–棧。泥瓦匠留給大家LinkedList的一些方法自己消化下。
package com.sedion.bysocket.collection;import java.util.LinkedList; /** * 用LinkedList實現棧 * 隊列和棧區別:隊列先進先出,棧先進後出。 */public class Stack<T>{ private LinkedList<T> storage = new LinkedList<T>(); /** 入棧 */ public void push(T v) { storage.addFirst(v); } /** 出棧,但不刪除 */ public T peek() { return storage.getFirst(); } /** 出棧,刪除 */ public T pop() { return storage.removeFirst(); } /** 棧是否為空白 */ public boolean empty() { return storage.isEmpty(); } /** 輸出棧元素 */ public String toString() { return storage.toString(); } public static void main(String[] args) { Stack stack=new Stack<String>(); stack.push("a"); stack.push("b"); stack.push("c"); System.out.println(stack.toString()); Object obj=stack.peek(); System.out.println(obj+"--"+stack.toString()); obj=stack.pop(); System.out.println(obj+"--"+stack.toString()); System.out.println(stack.empty()); }}四、總結
泥瓦匠總結如下:
Vector和ArrayList
1、vector是線程同步的,所以它也是安全執行緒的,而arraylist是線程非同步,是不安全的。
2、記住並發修改異常 java.util.ConcurrentModificationException ,優先考慮ArrayList,除非你在使用多線程所需。
Aarraylist和Linkedlist
1、對於隨機訪問get和set,ArrayList覺得優於LinkedList,LinkedList要移動指標。
2、於新增和刪除操作add和remove,LinedList比較佔優勢,ArrayList要移動資料。
3、
單條資料插入或刪除,ArrayList的速度反而優於LinkedList.原因是:LinkedList的資料結構是三個對象,組大小恰當就會比鏈錶快吧,直接賦值就完了,不用再設定前後指標的值。
若是批量隨機的插入刪除資料,LinkedList的速度大大優於ArrayList. 因為ArrayList每插入一條資料,要移動插入點及之後的所有資料。
轉載自:http://www.bysocket.com/?p=169
(轉載)Java 容器 & 泛型:二、ArrayList 、LinkedList和Vector比較