1. 程式人生 > >16、Collection介面及其子介面Set和List(常用類LinkedList,ArrayList,Vector和Stack)

16、Collection介面及其子介面Set和List(常用類LinkedList,ArrayList,Vector和Stack)

16、Collection介面
  Collection是最基本的集合介面,一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自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();              // 得到下一個元素
    }
16.1、Set介面

      Set是一種不包含重複元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。Set的建構函式有一個約束條件,傳入的Collection引數不能包含重複的元素。
  請注意:必須小心操作可變物件(Mutable Object)。如果一個Set中的可變元素改變了自身狀態,將導致Object.equals(Object)=true將導致一些問題。

16.2、List介面

      List是有序的Collection,使用此介面能夠精確的控制每個元素插入的位置。使用者能夠使用索引(元素在List中的位置,類似於陣列下標)來訪問List中的元素,類似於Java的陣列。和上面的Set不同,List允許有相同的元素。
  除了具有Collection介面必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator介面,和標準的Iterator介面相比,ListIterator多了一些add()之類的方法,允許新增,刪除,設定元素,還能向前或向後遍歷。

16.2.1、List介面常用類

AbstractList 是一個抽象類,它繼承於AbstractCollection。AbstractList實現List介面中除size()、get(int location)之外的函式。

AbstractSequentialList 是一個抽象類,它繼承於AbstractList。AbstractSequentialList 實現了“連結串列中,根據index索引值操作連結串列的全部函式”。

ArrayList 是一個數組佇列,相當於動態陣列。它由陣列實現,隨機訪問效率高,隨機插入、隨機刪除效率低。

LinkedList 是一個雙向連結串列。它也可以被當作堆疊、佇列或雙端佇列進行操作。LinkedList隨機訪問效率低,但隨機插入、隨機刪除效率低。

Vector 是向量佇列,和ArrayList一樣,它也是一個動態陣列,由陣列實現。但是ArrayList是非執行緒安全的,而Vector是執行緒安全的。

Stack 是棧,它繼承於Vector。它的特性是:先進後出(FILO, First In Last Out)。

16.2.1.1、LinkedList類

       LinkedList實現了List介面,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆疊(stack),佇列(queue)或雙向佇列(deque)。
  注意LinkedList沒有同步方法。如果多個執行緒同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在建立List時構造一個同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

16.2.1.2、ArrayList類
  ArrayList實現了可變大小的陣列。它允許所有元素,包括null。ArrayList沒有同步。size,isEmpty,get,set方法執行時間為常數。但是add方法開銷為分攤的常數,新增n個元素需要O(n)的時間。其他的方法執行時間為線性。
  每個ArrayList例項都有一個容量(Capacity),即用於儲存元素的陣列大小。這個容量可隨著不斷新增新元素而自動增加,但是增長演算法並沒有定義。當需要插入大量元素時,在插入前可以呼叫ensureCapacity方法來增加ArrayList的容量以提高插入效率。和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。

16.2.1.3、Vector類
  Vector非常類似ArrayList,但是Vector是同步的。由Vector建立的Iterator,雖然和ArrayList建立的Iterator是同一介面,但是,因為Vector是同步的,當一個Iterator被建立而且正在被使用,另一個執行緒改變了Vector的狀態(例如,新增或刪除了一些元素),這時呼叫Iterator的方法時將丟擲ConcurrentModificationException,因此必須捕獲該異常。
16.2.1.4、Stack 類
  Stack繼承自Vector,實現一個後進先出的堆疊。Stack提供5個額外的方法使得Vector得以被當作堆疊使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆疊是否為空,search方法檢測一個元素在堆疊中的位置。Stack剛建立後是空棧。

16.2.2、List介面使用場景

      如果涉及到“棧”、“佇列”、“連結串列”等操作,應該考慮用List,具體的選擇哪個List,根據下面的標準來取捨。

(01) 對於需要快速插入,刪除元素,應該使用LinkedList。

       通過add(int index, E element)向LinkedList插入元素時。先是在雙向連結串列中找到要插入節點的位置index;找到之後,再插入一個新節點。雙向連結串列查詢index位置的節點時,有一個加速動作:若index < 雙向連結串列長度的1/2,則從前向後查詢; 否則,從後向前查詢。

       ArrayList的add(int index, E element)函式,會引起index之後所有元素的改變,會將index之後所有元素都向後移動一位,從而使ArrayList中插入元素很慢。“刪除元素”與“插入元素”的原理類似。

(02) 對於需要快速隨機訪問元素,應該使用ArrayList。

       通過get(int index)獲取LinkedList第index個元素時。先是在雙向連結串列中找到index位置的元素,找到之後再返回。雙向連結串列查詢index位置的節點時,有一個加速動作:若index < 雙向連結串列長度的1/2,則從前向後查詢; 否則,從後向前查詢。

       通過get(int index)獲取ArrayList第index個元素時。直接返回陣列中index位置的元素,而不需要像LinkedList一樣進行查詢。

(03) 對於“單執行緒環境” 或者 “多執行緒環境,但List僅僅只會被單個執行緒操作”,此時應該使用非同步的類(如ArrayList)。

(04) 對於“多執行緒環境,且List可能同時被多個執行緒操作”,此時,應該使用同步的類(如Vector)。

16.2.2.1、Vector和ArrayList比較

相同之處:

     (1) 它們都是List,都繼承於AbstractList,並且實現List介面。

     (2) 它們都實現了RandomAccess和Cloneable介面。實現RandomAccess介面,意味著它們都支援快速隨機訪問;實現Cloneable介面,意味著它們能克隆自己。

     (3) 它們都是通過陣列實現的,本質上都是動態陣列。ArrayList.java中定義陣列elementData用於儲存元素;Vector.java中也定義了陣列elementData用於儲存元素

     (4) 它們的預設陣列容量是10。若建立ArrayList或Vector時,沒指定容量大小;則使用預設容量大小10。

     (5) 它們都支援Iterator和listIterator遍歷。它們都繼承於AbstractList,而AbstractList中分別實現了 “iterator()介面返回Iterator迭代器” 和 “listIterator()返回ListIterator迭代器”。

不同之處:

     (1) 執行緒安全性不一樣。ArrayList是非執行緒安全;而Vector是執行緒安全的,它的函式都是synchronized的,即都是支援同步的。ArrayList適用於單執行緒,Vector適用於多執行緒。

     (2) 對序列化支援不同。ArrayList支援序列化,而Vector不支援;即ArrayList有實現java.io.Serializable介面,而Vector沒有實現該介面。

     (3) 建構函式個數不同。ArrayList有3個建構函式,而Vector有4個建構函式。Vector除了包括和ArrayList類似的3個建構函式之外,另外的一個建構函式可以指定容量增加係數。

          ArrayList的建構函式如下:

// 預設建構函式
ArrayList()

// capacity是ArrayList的預設容量大小。當由於增加資料導致容量不足時,容量會新增上一次容量大小的一半。
ArrayList(int capacity)

// 建立一個包含collection的ArrayList
ArrayList(Collection<? extends E> collection)
          Vector的建構函式如下:
// 預設建構函式
Vector()

// capacity是Vector的預設容量大小。當由於增加資料導致容量增加時,每次容量會增加一倍。
Vector(int capacity)

// 建立一個包含collection的Vector
Vector(Collection<? extends E> collection)

// capacity是Vector的預設容量大小,capacityIncrement是每次Vector容量增加時的增量值。
Vector(int capacity, int capacityIncrement)

      (4) 容量增加方式不同。逐個新增元素時,若ArrayList容量不足時,“新的容量”=“(原始容量x3)/2 + 1”;而Vector的容量增長與“增長係數有關”,若指定了“增長係數”,且“增長係數有效(即,大於0)”;那麼,每次容量不足時,“新的容量”=“原始容量+增長係數”。若增長係數無效(即,小於/等於0),則“新的容量”=“原始容量 x 2”。

      (5) 對Enumeration的支援不同。Vector支援通過Enumeration去遍歷,而List不支援