Collection和Map總結
一、定義
集合框架是為表示和操作集合而規定的一種統一的標準的體系結構,在java中的集合框架主要分為兩部分:Collection介面和Map介面。
二、 用法
(一)集合框架涉及的資料結構
1.資料結構分類
1)線性表是在記憶體中資料的一種組織、儲存的方式;一維陣列、順序表、連結串列、棧、佇列,迴圈佇列、散列表等結構是邏輯上的概念,是一種理念與思想,是屬於線性表中的一種邏輯實現。
2)Java中資料結構涉及到了陣列(一維、多維)、順序表(ArrayList、Vector)、連結串列(LinkedArrayList、LinkedSet)、棧(stack)、佇列(Queue)、散列表(HashMap)、樹(TreeSet、TreeMap)等
(二)集合框架圖譜
1.集合框架主要實現類
2.集合框架類圖
1)Collection是最基本的集合介面,一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行,一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子介面”如List和Set。
2)所有實現Collection介面的類都必須提供兩個標準的建構函式:無引數的建構函式用於建立一個空的Collection,有一個Collection引數的建構函式用於建立一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。後一個建構函式允許使用者複製一個Collection。
3)為什麼繼承Iterable介面而不繼承Iterator?
因為Iterator介面的核心方法next()或者hasNext()是依賴於迭代器的當前迭代位置的。 如果Collection直接實現Iterator介面,勢必導致集合物件中包含當前迭代位置的資料(指標),當集合在不同方法間被傳遞時,由於當前迭代位置不可預置,那麼next()方法的結果會變成不可預知。 除非再為Iterator介面新增一個reset()方法,用來重置當前迭代位置。 但即使這樣,Collection也只能同時存在一個當前迭代位置,而Iterable則不然,每次呼叫都會返回一個從頭開始計數的迭代器。 多個迭代器是互不干擾的。
不同的Collection介面的子介面的實現類返回的Iterator具體型別可能不同,Array可能返回ArrayIterator,Set可能返回 SetIterator,Tree可能返回TreeIterator,但是它們都實現了Iterator介面。因此,客戶端不關心到底是哪種 Iterator,它只需要獲得這個Iterator介面即可,這就是面向物件的威力。
Iterator it = collection.iterator(); // 獲得一個迭代子
while(it.hasNext()){
Object obj = it.next(); // 得到下一個元素
}
(三)Collection介面
1.List介面
List是有序的Collection,使用此介面能夠精確的控制每個元素插入的位置。使用者能夠使用索引(元素在List中的位置,類似於陣列下標)來訪問List中的元素,這類似於Java的陣列。和下面要提到的Set不同,List允許有相同的元素。除了具Collection介面必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator介面,和標準的Iterator介面相比,ListIterator多了一些add()之類的方法,允許新增,刪除,設定元素,還能向前或向後遍歷。實現List介面的常用類有LinkedList,ArrayList,Vector和Stack。
1)ArrayList
ArrayList實現了可變大小的陣列,它允許所有元素,包括null。size,isEmpty,get,set方法執行時間為常數。但是add方法開銷為分攤的常數,新增n個元素需要O(n)的時間,其他的方法執行時間為線性。
每個ArrayList例項都有一個容量(Capacity),即用於儲存元素的陣列的大小,這個容量可隨著不斷新增新元素而自動增加,但是增長演算法並沒有定義。當需要插入大量元素時,在插入前可以呼叫ensureCapacity方法來增加ArrayList的容量以提高插入效率。和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
2)LinkedList
LinkedList實現了List介面,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆疊(stack),佇列(queue)或雙向佇列(deque),可以認為LinkedList在方法和邏輯上和ArrayList是一樣的,只是在效能上有一定的區別,ArrayList適合隨機訪問LinkedList更適合插入和刪除,在對效能沒有很大要求是是可以忽略這個差別的。
注意LinkedList沒有同步方法。如果多個執行緒同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在建立List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
3)Vector
Vector非常類似ArrayList,但是Vector是同步的。由Vector建立的Iterator,雖然和ArrayList建立的Iterator是同一介面,但是,因為Vector是同步的,當一個Iterator被建立而且正在被使用,另一個執行緒改變了Vector的狀態(例如,新增或刪除了一些元素),這時呼叫Iterator的方法時將丟擲ConcurrentModificationException,因此必須捕獲該異常。
ArrayList 和Vector是採用陣列方式儲存資料,此陣列元素數大於實際儲存的資料以便增加和插入元素,都允許直接序號索引元素,但是插入資料要設計到陣列元素移動等記憶體操作,所以索引資料快插入資料慢,Vector由於使用了synchronized方法(執行緒安全)所以效能上比ArrayList要差,LinkedList使用雙向連結串列實現儲存,按序號索引資料需要進行向前或向後遍歷,但是插入資料時只需要記錄本項的前後項即可,所以插入數度較快!
4)Stack
Stack繼承自Vector,實現一個後進先出的堆疊。Stack提供5個額外的方法使得Vector得以被當作堆疊使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆疊是否為空,search方法檢測一個元素在堆疊中的位置。Stack剛建立後是空棧。
2.Set介面
Set是一種不包括重複元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣執行null的存在但是僅有一個。由於Set介面的特殊性,所有傳入Set集合中的元素都必須不同,同時要注意任何可變物件,如果在對集合中元素進行操作時,導致e1.equals(e2)==true,則必定會產生某些問題。實現了Set介面的集合有:EnumSet、HashSet、TreeSet。
1)HashSet
HashSet堪稱查詢速度最快的集合,因為其內部是以HashCode來實現的。它內部元素的順序是由雜湊碼來決定的,所以它不保證set 的迭代順序,特別是它不保證該順序恆久不變。
2)TreeSet
基於TreeMap,生成一個總是處於排序狀態的set,內部以TreeMap來實現。它是使用元素的自然順序對元素進行排序,或者根據建立Set 時提供的Comparator進行排序,具體取決於使用的構造方法。
3)LinkedHashSet
底層是連結串列實現的,是set集合中唯一一個能保證怎麼存就怎麼取的集合物件,因為是HashSet的子類,所以也是保證元素唯一的,與HashSet的原理一樣。
3.Queue介面
1)阻塞式佇列(BlockingQueue):佇列滿了以後再插入元素則會丟擲異常,主要包括ArrayBlockQueue、PriorityBlockingQueue、LinkedBlockingQueue。
2)雙端佇列(Deque):支援在頭、尾兩端插入和移除元素,主要包括:ArrayDeque、LinkedBlockingDeque、LinkedList。
(四)Map介面
Map與List、Set介面不同,它是由一系列鍵值對組成的集合,提供了key到Value的對映。同時它也沒有繼承Collection。在Map中它保證了key與value之間的一一對應關係。也就是說一個key對應一個value,所以它不能存在相同的key值,當然value值可以相同。實現map的有:HashMap、TreeMap、Hashtable、Properties、EnumMap。
1)HashMap
HashMap和Hashtable類似,不同之處在於HashMap是非同步的,並且允許null,即null value和null key。,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例。因此,如果迭代操作的效能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低。
2)TreeMap
鍵以某種排序規則排序,內部以red-black(紅-黑)樹資料結構實現,實現了SortedMap介面
3)Hashtable
Hashtable繼承Dictionary類實現Map介面,實現一個key-value對映的雜湊表。任何非空(non-null)的物件都可作為key或者value。新增資料使用put(key,value),取出資料使用get(key),這兩個基本操作的時間開銷為常數。
Hashtable通過initial capacity和load factor兩個引數調整效能。通常預設的load factor 0.75較好地實現了時間和空間的均衡。增大load factor可以節省空間但相應的查詢時間將增大,這會影響像get和put這樣的操作。Hashtable是同步的。
三、總結
(一)集合型別的異同點
1.Vector和ArrayList
1)vector是執行緒同步的,所以它也是執行緒安全的,而arraylist是執行緒非同步的,是不安全的。如果不考慮到執行緒的安全因素,一般用arraylist效率比較高。
2)如果集合中的元素的數目大於目前集合陣列的長度時,vector增長率為目前陣列長度的100%,而arraylist增長率為目前陣列長度的50%.如過在集合中使用資料量比較大的資料,用vector有一定的優勢。
3)如果查詢一個指定位置的資料,vector和arraylist使用的時間是相同的,都是0(1),這個時候使用vector和arraylist都可以。而如果移動一個指定位置的資料花費的時間為0(n-i)n為總長度,這個時候就應該考慮到使用linklist,因為它移動一個指定位置的資料所花費的時間為0(1),而查詢一個指定位置的資料時花費的時間為0(i)。
4)ArrayList 和Vector是採用陣列方式儲存資料,此陣列元素數大於實際儲存的資料以便增加和插入元素,都允許直接序號索引元素,但是插入資料要設計到陣列元素移動等記憶體操作,所以索引資料快插入資料慢,Vector由於使用了synchronized方法(執行緒安全)所以效能上比ArrayList要差,LinkedList使用雙向連結串列實現儲存,按序號索引資料需要進行向前或向後遍歷,但是插入資料時只需要記錄本項的前後項即可,所以插入數度較快!
2.Aarraylist和Linkedlist
1)ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。
2)對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移動指標。
3)對於新增和刪除操作add和remove,LinedList比較佔優勢,因為ArrayList要移動資料。
4)這一點要看實際情況的。若只對單條資料插入或刪除,ArrayList的速度反而優於LinkedList。但若是批量隨機的插入刪除資料,LinkedList的速度大大優於ArrayList. 因為ArrayList每插入一條資料,要移動插入點及之後的所有資料。
3.HashMap與TreeMap
1)HashMap通過hashcode對其內容進行快速查詢,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。HashMap中元素的排列順序是不固定的)。
2)HashMap通過hashcode對其內容進行快速查詢,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。集合框架”提供兩種常規的Map實現:HashMap和TreeMap (TreeMap實現SortedMap介面)。
3)在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好。使用HashMap要求新增的鍵類明確定義了hashCode()和 equals()的實現。 這個TreeMap沒有調優選項,因為該樹總處於平衡狀態。
4.Hashtable與HashMap
1)歷史原因:Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map介面的一個實現 。
2)同步性:Hashtable是執行緒安全的,也就是說是同步的,而HashMap是執行緒序不安全的,不是同步的 。
3)值:只有HashMap可以讓你將空值作為一個表的條目的key或value 。
(二)如何選擇合適的集合型別
1.對List的選擇
1)對於隨機查詢與迭代遍歷操作,陣列比所有的容器都要快。所以在隨機訪問中一般使用ArrayList。
2)LinkedList使用雙向連結串列對元素的增加和刪除提供了非常好的支援,而ArrayList執行增加和刪除元素需要進行元素位移。
3)對於Vector而已,我們一般都是避免使用。
4)將ArrayList當做首選,畢竟對於集合元素而已我們都是進行遍歷,只有當程式的效能因為List的頻繁插入和刪除而降低時,再考慮LinkedList。
2.對Set的選擇
1)HashSet由於使用HashCode實現,所以在某種程度上來說它的效能永遠比TreeSet要好,尤其是進行增加和查詢操作。
2)雖然TreeSet沒有HashSet效能好,但是由於它可以維持元素的排序,所以它還是存在用武之地的。
3.對Map的選擇
1)HashMap與HashSet同樣,支援快速查詢。雖然HashTable速度的速度也不慢,但是在HashMap面前還是稍微慢了些,所以HashMap在查詢方面可以取代HashTable。
2)由於TreeMap需要維持內部元素的順序,所以它通常要比HashMap和HashTable慢。