集合框架之集合類概述
集合是一組複合元素的容器,用來儲存,檢索,控制聚合資料並提供它們之間的通訊方法。
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"); } }
除了上面提到的實現類之外,還有兩種不常用的實現類:Vector和Hashtable,它們是執行緒安全的。這是低版本JDK的遺留產物,對於集合而言,大多數情況都沒必要支援執行緒安全。在需要考慮多執行緒操作的環境下,JDK也提供了多種方法來將上面的通用實現類轉換成執行緒安全的類如果多個執行緒同時訪問一個實現類,而其中至少一個執行緒修改了該類,那麼它必須保持外部同步。這通常是通過對自然封裝該 set 的物件執行同步操作來完成的。如果不存在這樣的物件,則應該使用Collections.synchronizedMap方法來“包裝”map。最好在建立時完成這一操作,以防止對該 set 進行意外的不同步訪問:
Map map = Collections.synchronizedMap(new HashMap(...));