1. 程式人生 > >集合框架之集合類概述

集合框架之集合類概述

集合是一組複合元素的容器,用來儲存,檢索,控制聚合資料並提供它們之間的通訊方法。

java的集合框架提供了表示和操控集合的統一架構。所有的集合框架都包含下面三個方面:

介面:即集合的抽象資料結構。介面允許我們獨立地操縱集合而不用考慮集合的具體實現

實現:即介面的具體實現類。從本質上來講,它們是可重用的資料結構

演算法:即在實現了集合介面的物件上執行有用的計算,比如排序和搜尋,的方法。演算法是多型的:同名的方法可以被任何合適的介面實現類呼叫,從本質上來講,演算法是可重用的功能

核心集合介面封裝了不同型別的集合,它們是java集合框架的基礎,形成了下圖所示的層級結構


從上圖中可以看到,Set是一種特殊的Collection,而SortedSet是一種特殊的Set……諸如此類。

需要注意,上圖中有兩個不同的樹,Map並不是Collection的子介面,而是一個獨立的介面

所有的集合介面都是泛化的。比如下面是Collection介面的宣告:

public interface Collection<E>...

“<E>”表示該介面是通用的。當我們宣告一個Collection介面時,最好指定該介面包含的物件型別,以便讓編譯器在編譯時檢驗輸入的物件是否正確,從而減少執行時丟擲的錯誤。

對於介面中的方法,有很多是可選擇實現的,就是說,它的實現類可以實現該方法,也可以不實現,根據具體需要來決定

我們先來看一下核心的集合介面,對它們有一個整體的感性認識:

Collection介面

:集合框架的根介面。它是集合類框架中最具一般性的頂層介面。Java平臺沒有提供任何該介面的直接具體實現類,但是提供了具有各種不同特性的子介面

Set介面:不允許包含重複值的集合

List介面:可索引的集合,可以包含重複值。使用該介面時我們通過索引對元素進行精準的插入和查詢

Queue介面:該集合適用於組織一個佇列,佇列中的元素按照優先順序進行處理。除了繼承自Collection介面的方法,該介面還提供了另外的插入、提取和檢驗方法。典型的佇列是符合“先進先出”(FIFO:First In,First Out)原則的,優先順序佇列是一種例外,它按照元素的優先順序順序排列元素。無論按照什麼原則排序,隊頭元素總是首先被檢出。每個Queue介面的實現類必須指定它的排序原則

Deque介面:與Queue的不同之處在於它是一個雙端佇列,在兩端都能插入和移除元素,它繼承並擴充套件了Queue介面

Map介面:提供了鍵值對(key/value)的對映關係的集合。關鍵字不能有重複值,每個關鍵字至多可對映一個值

SortedSet介面:以升序的原則維持著集合中的元素順序。

SortedMap介面:以關鍵字升序的原則維持著集合中的元素順序

以上介面的通用實現類(這裡的通用實現類一般是指適用於單執行緒環境下的實現類,在JDK中有針對多執行緒併發環境下的特殊實現類)總結如下

介面

雜湊表實現

可變陣列實現

樹實現

連結串列實現

雜湊表+連結串列實現

堆實現

Set

HashSet

TreeSet

LinkedHashSet

List

ArrayList

LinkedList

Queue

LinkedList

PriorityQueue

Deque

LinkedList

Map

HashMap

TreeMap

LinkedHashMap

可以發現,LinkedList同時實現了List、Queue、Deque三個介面。

SortedSet介面和SortedMap介面沒有在上表中列出,在上面的層次結構圖中可以看到,它們分別是Set和Map的子介面,TreeSet和TreeMap就是它們的實現類

以上提到的所有通用實現類都支援為null的元素(或者鍵/值),不過有的只能包含一個null,有的可以包含多個null;所有通用實現類都實現了Serializable,是可序列化的物件;所有通用實現類都提供了一個clone方法用於複製物件;所有通用實現類都是執行緒不安全的(即是不同步的);所有通用實現類都提供了”fail-fast”機制的迭代器

關於“fail-fast”機制,來看一個例項:

             Map<String,String> map = new HashMap<String,String>();
             map.put("first", "Jay");
             map.put("second","Jack");
             map.put("third", "Jim");
            
             Iterator<String> it= map.keySet().iterator();
             while(it.hasNext()){
                    System.out.println(map.get(it.next()));
                    if(map.containsKey("second")){
                           map.remove("second");
                    }
             }
 

以上程式碼試圖使用Iterator迭代Map裡的鍵值,如果鍵值為“second”則刪除Map中的該元素。執行一下可以發現,執行刪除操作時會報java.util.ConcurrentModificationException異常,即便這是單執行緒。

Iterator 是工作在一個獨立的執行緒中,並且擁有一個 mutex 鎖。 Iterator 被建立之後會建立一個指向原來物件的單鏈索引表,當原來的物件數量發生變化時,這個索引表的內容不會同步改變,所以當索引指標往後移動的時候就找不到要迭代的物件,所以按照 fail-fast 原則 Iterator 會馬上丟擲 java.util.ConcurrentModificationException 異常。

但是,如果上面的程式碼進行的不是刪除操作,而是覆蓋掉原來的元素:

if(map.containsKey("second")){
      map.put ("second","Joe");
}

就不會報錯,上面的程式中的Iterator是對Map的Key進行迭代,雖然對Map進行了修改,但是隻改變了一個元素的value,整個Map的KeySet並沒發生變化

如果想要成功的執行刪除操作,就需要先對Iterator進行對應的刪除操作

             Map<String,String> map = new HashMap<String,String>();
             map.put("first","Jay");
             map.put("second","Jack");
             map.put("third","Jim");
            
             Iterator<String> it= map.keySet().iterator();
             while(it.hasNext()){
                    System.out.println(map.get(it.next()));
                    if(map.containsKey("second")){
                           it.remove();  //先對Iterator進行刪除
                           map.remove("second");
                    }
             }

除了上面提到的實現類之外,還有兩種不常用的實現類:VectorHashtable它們是執行緒安全的。這是低版本JDK的遺留產物,對於集合而言,大多數情況都沒必要支援執行緒安全。在需要考慮多執行緒操作的環境下,JDK也提供了多種方法來將上面的通用實現類轉換成執行緒安全的類

如果多個執行緒同時訪問一個實現類,而其中至少一個執行緒修改了該類,那麼它必須保持外部同步。這通常是通過對自然封裝該 set 的物件執行同步操作來完成的。如果不存在這樣的物件,則應該使用Collections.synchronizedMap方法來“包裝”map。最好在建立時完成這一操作,以防止對該 set 進行意外的不同步訪問:   

Map map = Collections.synchronizedMap(new HashMap(...));