Collection,List ,Set 和Map 用法和區別

首先看一下他們之間的關係 

Collection          接口的接口   對象的集合 
├ List                   子接口      按進入前後有序保存   可重複 
│├ LinkedList                接口實現類   鏈表   插入刪除   沒有同步   線程不安全 
│├ ArrayList                  接口實現類   數組   隨機訪問   沒有同步   線程不安全 
│└ Vector                      接口實現類   數組                  同步        線程安全 
│   └ Stack 
└ Set                   子接口       僅接收一次,並作內部排序 

├ HashSet 

│   └ LinkedHashSet 
└ TreeSet 


對於 List ,關心的是順序, 它保證維護元素特定的順序(容許有相同元素),使用此接口可以精確的控制每一個元素插入的位置。用戶可以使用索引(元素在 List 中的位置,相似於數組下標)來訪問 List 中的元素。 

對於 Set ,只關心某元素是否屬於 Set (不 容許有相同元素 ),而不關心它的順序。 

Map                接口      鍵值對的集合 
├ Hashtable                  接口實現類                  同步           線程安全 
├ HashMap                   接口實現類                  沒有同步    線程不安全 

│├ LinkedHashMap 

│└ WeakHashMap 

├ TreeMap 
└ IdentifyHashMap 

對於 Map ,最大的特色是鍵值映射,且爲一一映射,鍵不能重複,值能夠,因此是用鍵來索引值。 方法 put(Object key, Object value) 添加一個「值」 ( 想要得東西 ) 和與「值」相關聯的「鍵」 (key) ( 使用它來查找 ) 。方法 get(Object key) 返回與給定「鍵」相關聯的「值」。 

Map 一樣對每一個元素保存一份,但這是基於 " 鍵 " 的, Map 也有內置的排序,於是不關心元素添加的順序。若是添加元素的順序對你很重要,應該使用 LinkedHashSet 或者 LinkedHashMap. 

對於效率, Map 因爲採用了哈希散列,查找元素時明顯比 ArrayList 快。 

  
但我有一個本身的原則想法:複雜的問題簡單化。即把不少晦澀難懂的問題用通俗直白的話,一會兒就看明白了,而不是大段大段的寫。不得不指出的是現 在部分所謂的「專家」每每把簡單的問題複雜化,讓人看了生畏,甚至望而卻步,以此來顯示他的高深莫測,固然也可能有別的用意,那我就不得而知了。 


更爲精煉的總結: 

Collection 是對象集合, Collection 有兩個子接口 List 和 Set 

List 能夠經過下標 (1,2..) 來取得值,值能夠重複 

而 Set 只能經過遊標來取值,而且值是不能重複的 

ArrayList , Vector , LinkedList 是 List 的實現類 

ArrayList 是線程不安全的, Vector 是線程安全的,這兩個類底層都是由數組實現的 

LinkedList 是線程不安全的,底層是由鏈表實現的   

Map 是鍵值對集合 

HashTable 和 HashMap 是 Map 的實現類   
HashTable 是線程安全的,不能存儲 null 值   
HashMap 不是線程安全的,能夠存儲 null 值   
  

因此,若是你是想在一個很短的時間來弄明白這些問題,好比 1~2 分鐘。沒有也不想花大量時間於此,那麼建議你如今就能夠收兵走人了。 

  
若是你想對此作一個詳細的瞭解,請繼續看下去。 

  
衆所周知, Java 來源於 C++ ,屏蔽了其底層實現,簡化了對底層實現的管理,使開發者專一於上層功能的實現。在 C/C++ 裏關於數據的存儲須要程序員很是清楚,而 Java 程序員能夠徹底無論這些,那麼, Java 是怎麼管理的呢?其實 Java 仍是須要面臨這些問題,只不過通過封裝後,變得面目全非。因此對於像我這種從 C/C++ 轉向 Java 的人還須要一段時間適應, Collection 、 List 、 Set 、 Map 等概念還須要一個接受的過程。其實到後來發現,無論是什麼語言,其底層存儲不外乎數組、線性表、棧、隊列、串、樹和圖等數據結構。想明白了這些,一切都敞 亮了。 

  
1、容器( Collection ) 接口 
   容器( Collection )是最基本的集合接口,一個容器( Collection )保存一組對象( Object ),即對象是容器的元素( 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(); // 獲得下一個元素 
     } 


由 Collection 接口派生的兩個接口是 List 和 Set 。 List 按對象進入的順序保存對象,不作排序或編輯操做。 Set 對每一個對象只接受一次,並使用本身內部的排序方法 ( 一般,你只關心某個元素是否屬於 Set, 而不關心它的順序 -- 不然應該使用 List) 。 


1 , List 接口 
   List 是有序的 Collection ,次序是 List 最重要的特色:它保證維護元素特定的順序。使用此接口可以精確的控制每一個元素插入的位置。用戶可以使用索引(元素在 List 中的位置,相似於數組下標)來訪問 List 中的元素,這相似於 Java 的數組。和下面要提到的 Set 不一樣, List 容許有相同的元素。 
   除了具備 Collection 接口必備的 iterator() 方法外, List 還提供一個 listIterator() 方法,返回一個 ListIterator 接口,和標準的 Iterator 接口相比, ListIterator 多了一些 add() 之類的方法,容許添加,刪除,設定元素, 還能向前或向後遍歷。 
  實現 List 接口的經常使用類有 LinkedList , ArrayList , Vector 和 Stack 。其中,最經常使用的是 LinkedList 和 ArrayList 兩個。 

LinkedList 類 
    LinkedList 實現了 List 接口,容許 null 元素。此外 LinkedList 提供額外的 addFirst(), addLast(), getFirst(), getLast(), removeFirst(), removeLast(), insertFirst(), insertLast() 方法在 LinkedList 的首部或尾部,這些方法(沒有在任何接口或基類中定義過)使 LinkedList 可被用做堆棧( stack ),隊列( queue )或雙向隊列( deque )。 

注意 LinkedList 沒有同步方法。若是多個線程同時訪問一個 List ,則必須本身實現訪問同步。一種解決方法是在建立 List 時構造一個同步的 List : 
     List list = Collections.synchronizedList(new LinkedList(...)); 

特色:對順序訪問進行了優化,向 List 中間插入與刪除的開銷並不大。隨機訪問則相對較慢。 ( 使用 ArrayList 代替。 ) 

ArrayList 類 
   ArrayList 是由數組實現的 List ,而且實現了可變大小的數組。它容許全部元素,包括 null 。 ArrayList 沒有同步。 size , isEmpty , get , set 方法運行時間爲常數。可是 add 方法開銷爲分攤的常數,添加 n 個元素須要 O(n) 的時間。其餘的方法運行時間爲線性。 
   每一個 ArrayList 實例都有一個容量( Capacity ),即用於存儲元素的數組的大小。這個容量可隨着不斷添加新元素而自動增長,可是增加算法並無定義。當須要插入大量元素時,在插入前能夠調用 ensureCapacity 方法來增長 ArrayList 的容量以提升插入效率。 
  和 LinkedList 同樣, ArrayList 也是非同步的( unsynchronized )。 

  
特色:容許對元素進行快速隨機訪問,可是向 List 中間插入與移除元素的速度很慢。 ListIterator 只應該用來由後向前遍歷 ArrayList, 而不是用來插入和移除元素。由於那比 LinkedList 開銷要大不少。 

  
Vector 類 
    Vector 很是相似 ArrayList ,可是 Vector 是同步的。由 Vector 建立的 Iterator ,雖然和 ArrayList 建立的 Iterator 是同一接口,可是,由於 Vector 是同步的,當一個 Iterator 被建立並且正在被使用,另外一個線程改變了 Vector 的狀態(例如,添加或刪除了一些元素),這時調用 Iterator 的方法時將拋出 ConcurrentModificationException ,所以必須捕獲該異常。 

     Stack 類: Stack 繼承自 Vector ,實現一個後進先出的堆棧。 Stack 提供 5 個額外的方法使得 Vector 得以被看成堆棧使用。基本的 push 和 pop 方法,還有 peek 方法獲得棧頂的元素, empty 方法測試堆棧是否爲空, search 方法檢測一個元素在堆棧中的位置。 Stack 剛建立後是空棧。 

2 , Set 接口 
   Set 具備與 Collection 徹底同樣的接口,所以沒有任何額外的功能,不像前面有幾個不一樣的 List 。實際上 Set 就是 Collection ,只是行爲不一樣。(這是繼承與多態思想的典型應用:表現不一樣的行爲)。其次, Set 是一種不包含重複的元素的 Collection ,加入 Set 的元素必須定義 equals() 方法以確保對象的惟一性 ( 即任意的兩個元素 e1 和 e2 都有 e1.equals(e2)=false ),與 List 不一樣的是, Set 接口不保證維護元素的次序。最後, Set 最多有一個 null 元素。 
  很明顯, Set 的構造函數有一個約束條件,傳入的 Collection 參數不能包含重複的元素。 
  請注意:必須當心操做可變對象( Mutable Object )。若是一個 Set 中的可變元素改變了自身狀態致使 Object.equals(Object)=true 將致使一些問題。 


HashSet 類 

爲快速查找設計的 Set 。存入 HashSet 的對象必須定義 hashCode() 。 

LinkedHashSet 類:具備 HashSet 的查詢速度,且內部使用鏈表維護元素的順序 ( 插入的次序 ) 。因而在使用迭代器遍歷 Set 時,結果會按元素插入的次序顯示。 

TreeSet 類 

保存次序的 Set, 底層爲樹結構。使用它能夠從 Set 中提取有序的序列。 


2、 Map 接口 





經常使用操做說明 

  void clear() 

  今後映射中移除全部映射關係(可選操做)。 

  boolean containsKey(Object key) 

  若是此映射包含指定鍵的映射關係,則返回 true。 

  boolean containsValue(Object value) 

  若是此映射將一個或多個鍵映射到指定值,則返回 true。 

  Set<Map.Entry<K,V>> entrySet() 

  返回此映射中包含的映射關係的 Set 視圖。 

  boolean equals(Object o) 

  比較指定的對象與此映射是否相等。 

  V get(Object key) 

  返回指定鍵所映射的值;若是此映射不包含該鍵的映射關係,則返回 null。 

  int hashCode() 

  返回此映射的哈希碼值。 

  boolean isEmpty() 

  若是此映射未包含鍵-值映射關係,則返回 true。 

  Set<K> keySet() 

  返回此映射中包含的鍵的 Set 視圖。 

  V put(K key, V value) 

  將指定的值與此映射中的指定鍵關聯(可選操做)。 

  void putAll(Map<? extends K,? extends V> m) 

  從指定映射中將全部映射關係複製到此映射中(可選操做)。 

  V remove(Object key) 

  若是存在一個鍵的映射關係,則將其今後映射中移除(可選操做)。 

  int size() 

  返回此映射中的鍵-值映射關係數。 

  Collection<V> values() 

  返回此映射中包含的值的 Collection 視圖。 

Map的通常用法 

1.聲明一個Map : 

   Map map = new HashMap(); 

2 .向map中放值 ,注意: map是key-value的形式存放的,如: 

       map.put("sa","dd"); 

3 .從map中取值 : 

    String str = map.get("sa").toString, 

    結果是: str = "dd' 

4 .遍歷一個map,從中取得key和value : 

    Map m= new HashMap(); 

    for(Object obj : map.keySet()){ 

          Object value = map.get(obj ); 

    } 


   請注意, Map 沒有繼承 Collection 接口, Map 提供 key 到 value 的映射,你能夠經過「鍵」查找「值」。一個 Map 中不能包含相同的 key ,每一個 key 只能映射一個 value 。 Map 接口提供 3 種集合的視圖, Map 的內容能夠被看成一組 key 集合,一組 value 集合,或者一組 key-value 映射。 

方法 put(Object key, Object value) 添加一個「值」 ( 想要得東西 ) 和與「值」相關聯的「鍵」 (key) ( 使用它來查找 ) 。方法 get(Object key) 返回與給定「鍵」相關聯的「值」。能夠用 containsKey() 和 containsValue() 測試 Map 中是否包含某個「鍵」或「值」。 標準的 Java 類庫中包含了幾種不一樣的 Map : HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap 。它們都有一樣的基本接口 Map ,可是行爲、效率、排序策略、保存對象的生命週期和斷定「鍵」等價的策略等各不相同。 

Map 一樣對每一個元素保存一份,但這是基於 " 鍵 " 的, Map 也有內置的排序,於是不關心元素添加的順序。若是添加元素的順序對你很重要,應該使用 LinkedHashSet 或者 LinkedHashMap. 

執行效率是 Map 的一個大問題。看看 get() 要作哪些事,就會明白爲何在 ArrayList 中搜索「鍵」是至關慢的。而這正是 HashMap 提升速度的地方。 HashMap 使用了特殊的值,稱爲「散列碼」 (hash code) ,來取代對鍵的緩慢搜索。「散列碼」是「相對惟一」用以表明對象的 int 值,它是經過將該對象的某些信息進行轉換而生成的(在下面總結二:須要的注意的地方有更進一步探討)。全部 Java 對象都能產生散列碼,由於 hashCode() 是定義在基類 Object 中的方法 。 HashMap 就是使用對象的 hashCode() 進行快速查詢的。此方法可以顯著提升性能。 


Hashtable 類 
   Hashtable 繼承 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 , 2 , 3 放到 Hashtable 中,他們的 key 分別是 」one」 , 」two」 , 」three」 : 
     Hashtable numbers = new Hashtable(); 
     numbers.put(「one」, new Integer(1)); 
     numbers.put(「two」, new Integer(2)); 
     numbers.put(「three」, new Integer(3)); 
  要取出一個數,好比 2 ,用相應的 key : 
     Integer n = (Integer)numbers.get(「two」); 
     System.out.println(「two = 」 + n); 
   因爲做爲 key 的對象將經過計算其散列函數來肯定與之對應的 value 的位置,所以任何做爲 key 的對象都必須實現 hashCode 方法和 equals 方法。 hashCode 方法和 equals 方法繼承自根類 Object ,若是你用自定義的類看成 key 的話,要至關當心,按照散列函數的定義,若是兩個對象相同,即 obj1.equals(obj2)=true ,則它們的 hashCode 必須相同,但若是兩個對象不一樣,則它們的 hashCode 不必定不一樣,若是兩個不一樣對象的 hashCode 相同,這種現象稱爲衝突,衝突會致使操做哈希表的時間開銷增大,因此儘可能定義好的 hashCode() 方法,能加快哈希表的操做。 
  若是相同的對象有不一樣的 hashCode ,對哈希表的操做會出現意想不到的結果(期待的 get 方法返回 null ),要避免這種問題,只須要牢記一條:要同時複寫 equals 方法和 hashCode 方法,而不要只寫其中一個。 
   Hashtable 是同步的。 

HashMap 類 
    HashMap 和 Hashtable 相似,也是基於散列表的實現。不一樣之處在於 HashMap 是非同步的,而且容許 null ,即 null value 和 null key 。將 HashMap 視爲 Collection 時( values() 方法可返回 Collection ),插入和查詢「鍵值對」的開銷是固定的,但其迭代子操做時間開銷和 HashMap 的容量成比例。所以,若是迭代操做的性能至關重要的話,不要將 HashMap 的初始化容量 (initial capacity) 設得太高,或者負載因子 (load factor) 太低。 

   LinkedHashMap 類:相似於 HashMap ,可是迭代遍歷它時,取得「鍵值對」的順序是其插入次序,或者是最近最少使用 (LRU) 的次序。只比 HashMap 慢一點。而在迭代訪問時發而更快,由於它使用鏈表維護內部次序。 

WeakHashMap 類:弱鍵( weak key ) Map 是一種改進的 HashMap ,它是爲解決特殊問題設計的,對 key 實行 「 弱引用 」 ,若是一個 key 再也不被外部所引用(沒有 map 以外的引用),那麼該 key 能夠被垃圾收集器 (GC) 回收。 

TreeMap 類 

基於紅黑樹數據結構的實現。查看「鍵」或「鍵值對」時,它們會被排序 ( 次序由 Comparabel 或 Comparator 決定 ) 。 TreeMap 的特色在於,你獲得的結果是通過排序的。 TreeMap 是惟一的帶有 subMap() 方法的 Map ,它能夠返回一個子樹。 

IdentifyHashMap 類 

使用 == 代替 equals() 對「鍵」做比較的 hash map 。專爲解決特殊問題而設計。 


總結一:比較 

1 ,數組 (Array) ,數組類 (Arrays) 

Java 全部「存儲及隨機訪問一連串對象」的作法, array 是最有效率的一種。但缺點是容量固定且沒法動態改變。 array 還有一個缺點是,沒法判斷其中實際存有多少元素, length 只是告訴咱們 array 的容量。 


Java 中有一個數組類 (Arrays) ,專門用來操做 array 。數組類 (arrays) 中擁有一組 static 函數。 

equals() :比較兩個 array 是否相等。 array 擁有相同元素個數,且全部對應元素兩兩相等。 

fill() :將值填入 array 中。 

sort() :用來對 array 進行排序。 

binarySearch() :在排好序的 array 中尋找元素。 

System.arraycopy() : array 的複製。 

  
若編寫程序時不知道究竟須要多少對象,須要在空間不足時自動擴增容量,則須要使用容器類庫, array 不適用。 

  
2 ,容器類與數組的區別 

容器類僅能持有對象引用(指向對象的指針),而不是將對象信息 copy 一份至數列某位置。一旦將對象置入容器內,便損失了該對象的型別信息。 


3 ,容器 (Collection) 與 Map 的聯繫與區別 

Collection 類型,每一個位置只有一個元素。 

Map 類型,持有 key-value 對 (pair) ,像個小型數據庫。 

  
Collections 是針對集合類的一個幫助類。提供了一系列靜態方法實現對各類集合的搜索、排序、線程徹底化等操做。至關於對 Array 進行相似操做的類—— Arrays 。 

如, Collections.max(Collection coll); 取 coll 中最大的元素。 

    Collections.sort(List list); 對 list 中元素排序 

  
List , Set , Map 將持有對象一概視爲 Object 型別。 

Collection 、 List 、 Set 、 Map 都是接口,不能實例化。繼承自它們的 ArrayList, Vector, HashTable, HashMap 是具象 class ,這些纔可被實例化。 

vector 容器確切知道它所持有的對象隸屬什麼型別。 vector 不進行邊界檢查。 


總結二:須要注意的地方 

1 、 Collection 只能經過 iterator() 遍歷元素,沒有 get() 方法來取得某個元素。 

2 、 Set 和 Collection 擁有如出一轍的接口。但排除掉傳入的 Collection 參數重複的元素。 

3 、 List ,能夠經過 get() 方法來一次取出一個元素。使用數字來選擇一堆對象中的一個, get(0)... 。 (add/get) 

4 、 Map 用 put(k,v) / get(k) ,還可使用 containsKey()/containsValue() 來檢查其中是否含有某個 key/value 。 

HashMap 會利用對象的 hashCode 來快速找到 key 。 

哈希碼 (hashing) 就是將對象的信息通過一些轉變造成一個獨一無二的 int 值,這個值存儲在一個 array 中。咱們都知道全部存儲結構中, array 查找速度是最快的。因此,能夠加速查找。發生碰撞時,讓 array 指向多個 values 。即,數組每一個位置上又生成一個槤表。 

5 、 Map 中元素,能夠將 key 序列、 value 序列單獨抽取出來。 

使用 keySet() 抽取 key 序列,將 map 中的全部 keys 生成一個 Set 。 

使用 values() 抽取 value 序列,將 map 中的全部 values 生成一個 Collection 。 

爲何一個生成 Set ,一個生成 Collection ?那是由於, key 老是獨一無二的, value 容許重複。 


總結三:如何選擇 
從效率角度: 

在各類 Lists ,對於須要快速插入,刪除元素,應該使用 LinkedList (可用 LinkedList 構造堆棧 stack 、隊列 queue ),若是須要快速隨機訪問元素,應該使用 ArrayList 。最好的作法是以 ArrayList 做爲缺省選擇。 Vector 老是比 ArrayList 慢,因此要儘可能避免使用。 

在各類 Sets 中, HashSet 一般優於 HashTree (插入、查找)。只有當須要產生一個通過排序的序列,才用 TreeSet 。 HashTree 存在的惟一理由:可以維護其內元素的排序狀態。 

  
在各類 Maps 中 HashMap 用於快速查找。 

最後,當元素個數固定,用 Array ,由於 Array 效率是最高的。 

因此結論:最經常使用的是 ArrayList , HashSet , HashMap , Array 。 

  
更近一步分析: 

若是程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高,若是多個線程可能同時操做一個類,應該使用同步的類。 
要特別注意對哈希表的操做,做爲 key 的對象要同時正確複寫 equals 方法和 hashCode 方法。 
儘可能返回接口而非實際的類型,如返回 List 而非 ArrayList ,這樣若是之後須要將 ArrayList 換成 LinkedList 時,客戶端代碼不用改變。這就是針對抽象編程。程序員

 

企業級項目實戰(帶源碼)地址http://zz563143188.iteye.com/blog/1825168算法

收集五年的開發資料下載地址:   http://pan.baidu.com/share/home?uk=4076915866&view=share
http://blog.csdn.net/jeamking/article/details/5836722
相關文章
相關標籤/搜索