java中各種集合的用法

來源:互聯網
上載者:User

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

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 , 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(newLinkedList(...));

特點:對順序訪問進行了最佳化,向 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 中提取有序的序列。

 

 

二、 Map介面
   請注意, 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 使用了特殊的值,稱為“散列碼” (hashcode) ,來取代對鍵的緩慢搜尋。“散列碼”是“相對唯一”用以代表對象的 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) 兩個參數調整效能。通常預設的 loadfactor 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://blog.sina.com.cn/s/blog_67d81e6a0100kzfq.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.