1. 程式人生 > >Java中Map, List, Set和Queue的區別和使用場景

Java中Map, List, Set和Queue的區別和使用場景

1. Java集合類基本概念

在程式設計中,常常需要集中存放多個數據。從傳統意義上講,陣列是我們的一個很好的選擇,前提是我們事先已經明確知道我們將要儲存的物件的數量。一旦在陣列初始化時指定了這個陣列長度,這個陣列長度就是不可變的,如果我們需要儲存一個可以動態增長的資料(在編譯時無法確定具體的數量),java的集合類就是一個很好的設計方案了。

集合類主要負責儲存、盛裝其他資料,因此集合類也被稱為容器類。所以的集合類都位於java.util包下,後來為了處理多執行緒環境下的併發安全問題,java5還在java.util.concurrent包下提供了一些多執行緒支援的集合類。

Java容器類類庫的用途是"儲存物件",並將其劃分為兩個不同的概念:

1) Collection
一組"對立"的元素,通常這些元素都服從某種規則
   1.1) List必須保持元素特定的順序
   1.2) Set不能有重複元素
   1.3) Queue保持一個佇列(先進先出)的順序
2) Map

一組成對的"鍵值對"物件

Collection和Map的區別在於容器中每個位置儲存的元素個數:

1) Collection 每個位置只能儲存一個元素(物件)

2) Map儲存的是"鍵值對",就像一個小型資料庫。我們可以通過"鍵"找到該鍵對應的"值"

2. Java集合類架構層次關係

1. Interface Iterable

迭代器介面,這是Collection
類的父介面。實現這個Iterable介面的物件允許使用foreach進行遍歷,也就是說,所有的Collection集合物件都具有"foreach可遍歷性"。這個Iterable介面只
有一個方法: iterator()。它返回一個代表當前集合物件的泛型<T>迭代器,用於之後的遍歷操作

1.1 Collection

Collection是最基本的集合介面,一個Collection代表一組Object的集合,這些Object被稱作Collection的元素。Collection是一個介面,用以提供規範定義,不能被例項化使用

    1) Set

    Set集合類似於一個罐子,"丟進"Set集合裡的多個物件之間沒有明顯的順序。Set繼承自Collection介面,不能包含有重複元素
(記住,這是整個Set類層次的共有屬性)。
    Set判斷兩個物件相同不是使用"=="運算子,而是根據equals方法。也就是說,我們在加入一個新元素的時候,如果這個新元素物件和Set中已有物件進行注意equals比較都返回false,則Set就會接受這個新元素物件,否則拒絕。
    因為Set的這個制約,在使用Set集合的時候,應該注意兩點:
1) 為Set集合裡的元素的實現類實現一個有效的equals(Object)方法、 2) 對Set的建構函式,傳入的Collection引數不能包含重複的元素
        1.1) HashSet
        HashSet是Set介面的典型實現,HashSet使用HASH演算法來儲存集合中的元素,因此具有良好的存取和查詢效能。當向HashSet集合中存入一個元素時,HashSet會呼叫該物件的hashCode()方法來得到該物件的hashCode值,然後根據該HashCode值決定該物件在HashSet中的儲存位置。
        值得注意的是,HashSet集合判斷兩個元素相等的標準是兩個物件通過equals()方法比較相等,並且兩個物件的hashCode()方法的返回值相等
            1.1.1) LinkedHashSet
            LinkedHashSet集合也是根據元素的hashCode值來決定元素的儲存位置,但和HashSet不同的是,它同時使用連結串列維護元素的次序,這樣使得元素看起來是以插入的順序儲存的。
    當遍歷LinkedHashSet集合裡的元素時,LinkedHashSet將會按元素的新增順序來訪問集合裡的元素。LinkedHashSet需要維護元素的插入順序,因此效能略低於HashSet的效能,但在迭代訪問Set裡的全部元素時(遍歷)將有很好的效能(連結串列很適合進行遍歷)
        1.2) SortedSet    
        此介面主要用於排序操作,即實現此介面的子類都屬於排序的子類
            1.2.1) TreeSet
            TreeSet是SortedSet介面的實現類,TreeSet可以確保集合元素處於排序狀態
        1.3) EnumSet
        EnumSet是一個專門為列舉類設計的集合類,EnumSet中所有元素都必須是指定列舉型別的列舉值,該列舉型別在建立EnumSet時顯式、或隱式地指定。EnumSet的集合元素也是有序的,
   它們以列舉值在Enum類內的定義順序來決定集合元素的順序

    2) List

    List集合代表一個元素有序、可重複的集合,集合中每個元素都有其對應的順序索引。List集合允許加入重複元素,因為它可以通過索引來訪問指定位置的集合元素。List集合預設按元素的新增順序設定元素的索引
        2.1) ArrayList
        ArrayList是基於陣列實現的List類,它封裝了一個動態的增長的、允許再分配的Object[]陣列。
        2.2) Vector
        Vector和ArrayList在用法上幾乎完全相同,但由於Vector是一個古老的集合,所以Vector提供了一些方法名很長的方法,但隨著JDK1.2以後,java提供了系統的集合框架,就將Vector改為實現List介面,統一歸入集合框架體系中
            2.2.1) Stack
            Stack是Vector提供的一個子類,用於模擬""這種資料結構(LIFO後進先出)
        2.3) LinkedList
        implements List<E>, Deque<E>。實現List介面,能對它進行佇列操作,即可以根據索引來隨機訪問集合中的元素。同時它還實現Deque介面,即能將LinkedList當作雙端佇列使用。自然也可以被當作"棧來使用"

    3) Queue

    Queue用於模擬"佇列"這種資料結構(先進先出 FIFO)。佇列的頭部儲存著佇列中存放時間最長的元素,佇列的尾部儲存著佇列中存放時間最短的元素。新元素插入(offer)到佇列的尾部,訪問元素(poll)操作會返回佇列頭部的元素,佇列不允許隨機訪問佇列中的元素。結合生活中常見的排隊就會很好理解這個概念
        3.1) PriorityQueue
        PriorityQueue並不是一個比較標準的佇列實現,PriorityQueue儲存佇列元素的順序並不是按照加入佇列的順序,而是按照佇列元素的大小進行重新排序,這點從它的類名也可以看出來
        3.2) Deque
        Deque介面代表一個"雙端佇列",雙端佇列可以同時從兩端來新增、刪除元素,因此Deque的實現類既可以當成佇列使用、也可以當成棧使用
            3.2.1) ArrayDeque
            是一個基於陣列的雙端佇列,和ArrayList類似,它們的底層都採用一個動態的、可重分配的Object[]陣列來儲存集合元素,當集合元素超出該陣列的容量時,系統會在底層重新分配一個Object[]陣列來儲存集合元素
            3.2.2) LinkedList

1.2 Map

    Map用於儲存具有"對映關係"的資料,因此Map集合裡儲存著兩組值,一組值用於儲存Map裡的key,另外一組值用於儲存Map裡的value。key和value都可以是任何引用型別的資料。Map的key不允許重複,即同一個Map物件的任何兩個key通過equals方法比較結果總是返回false。
    關於Map,我們要從程式碼複用的角度去理解,java是先實現了Map,然後通過包裝了一個所有value都為null的Map就實現了Set集合。Map的這些實現類和子介面中key集的儲存形式和Set集合完全相同(即key不能重複)
    Map的這些實現類和子介面中value集的儲存形式和List非常類似(即value可以重複、根據索引來查詢)

    1) HashMap

    和HashSet集合不能保證元素的順序一樣,HashMap也不能保證key-value對的順序。並且類似於HashSet判斷兩個key是否相等的標準也是: 兩個key通過equals()方法比較返回true、同時兩個key的hashCode值也必須相等
        1.1) LinkedHashMap
        LinkedHashMap也使用雙向連結串列來維護key-value對的次序,該連結串列負責維護Map的迭代順序,與key-value對的插入順序一致(注意和TreeMap對所有的key-value進行排序進行區分)

    2) Hashtable

    是一個古老的Map實現類
        2.1) Properties 
        Properties物件在處理屬性檔案時特別方便(windows平臺上的.ini檔案),Properties類可以把Map物件和屬性檔案關聯起來,從而可以把Map物件中的key-value對寫入到屬性檔案中,也可以把屬性檔案中的"屬性名-屬性值"載入到Map物件中

    3) SortedMap

    正如Set介面派生出SortedSet子介面,SortedSet介面有一個TreeSet實現類一樣,Map介面也派生出一個SortedMap子介面,SortedMap介面也有一個TreeMap實現類
        3.1) TreeMap
        TreeMap就是一個紅黑樹資料結構,每個key-value對即作為紅黑樹的一個節點。TreeMap儲存key-value對(節點)時,需要根據key對節點進行排序。TreeMap可以保證所有的key-value對處於有序狀態。同樣,TreeMap也有兩種排序方式: 自然排序、定製排序

    4) WeakHashMap

    WeakHashMap與HashMap的用法基本相似。區別在於,HashMap的key保留了對實際物件的"強引用",這意味著只要該HashMap物件不被銷燬,該HashMap所引用的物件就不會被垃圾回收。
  但WeakHashMap的key只保留了對實際物件的弱引用,這意味著如果WeakHashMap物件的key所引用的物件沒有被其他強引用變數所引用,則這些key所引用的物件可能被垃圾回收,當垃圾回收了該key所對應的實際物件之後,WeakHashMap也可能自動刪除這些key所對應的key-value對

    5) IdentityHashMap

    IdentityHashMap的實現機制與HashMap基本相似,在IdentityHashMap中,當且僅當兩個key嚴格相等(key1 == key2)時,IdentityHashMap才認為兩個key相等

    6) EnumMap

    EnumMap是一個與列舉類一起使用的Map實現,EnumMap中的所有key都必須是單個列舉類的列舉值。建立EnumMap時必須顯式或隱式指定它對應的列舉類。EnumMap根據key的自然順序

3. Java集合類使用場景規則

Set集合:
1) HashSet的效能總是比TreeSet好(特別是最常用的新增、查詢元素等操作),因為TreeSet需要額外的紅黑樹演算法來維護集合元素的次序。只有當需要一個保持排序的Set時,才應該使用TreeSet,否則都應該使用HashSet
2) 對於普通的插入、刪除操作,LinkedHashSet比HashSet要略慢一點,這是由維護連結串列所帶來的開銷造成的。不過,因為有了連結串列的存在,遍歷LinkedHashSet會更快
3) EnumSet是所有Set實現類中效能最好的,但它只能儲存同一個列舉類的列舉值作為集合元素
4) HashSet、TreeSet、EnumSet都是"執行緒不安全"的,通常可以通過Collections工具類的synchronizedSortedSet方法來"包裝"該Set集合。
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

List集合:
1. java提供的List就是一個"線性表介面",ArrayList(基於陣列的線性表)、LinkedList(基於鏈的線性表)是線性表的兩種典型實現
2. Queue代表了佇列,Deque代表了雙端佇列(既可以作為佇列使用、也可以作為棧使用)
3. 因為陣列以一塊連續記憶體來儲存所有的陣列元素,所以陣列在隨機訪問時效能最好。所以的內部以陣列作為底層實現的集合在隨機訪問時效能最好。
4. 內部以連結串列作為底層實現的集合在執行插入、刪除操作時有很好的效能
5. 進行迭代操作時,以連結串列作為底層實現的集合比以陣列作為底層實現的集合效能好

Map集合:
1) HashMap和Hashtable的效率大致相同,因為它們的實現機制幾乎完全一樣。但HashMap通常比Hashtable要快一點,因為Hashtable需要額外的執行緒同步控制
2) TreeMap通常比HashMap、Hashtable要慢(尤其是在插入、刪除key-value對時更慢),因為TreeMap底層採用紅黑樹來管理key-value對
3) 使用TreeMap的一個好處就是: TreeMap中的key-value對總是處於有序狀態,無須專門進行排序操作