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不支援