集合作為java的基礎知識,本來感覺自己理解的很清楚了,但是在最近的一次面試中還是答得不盡如人意!再次做一下整理,以便加深理解以及隨時查閱。
首先,java.util包中三個重要的接口及特點:List(列表)、Set(保證集合中元素唯一)、Map(維護多個key-value鍵值對,保證key唯一)。
集合框架體系如下圖所示:
圖1
各個集合類型的區別與聯系如下圖:
接口 | 簡述 | 實現 | 操作特性 | 成員要求 |
Set | 成員不能重復 | HashSet | 外部無序地遍歷成員 | 成員可為任意Object子類的對象,但如果覆蓋了equals方法,同時注意修改hashCode方法。 |
TreeSet | 外部有序地遍歷成員;附加實現了SortedSet, 支持子集等要求順序的操作 | 成員要求實現caparable接口,或者使用 Comparator構造TreeSet。成員一般為同一類型。 | ||
LinkedHashSet | 外部按成員的插入順序遍歷成員 | 成員與HashSet成員類似 | ||
List | 提供基于索引的對成員的隨機訪問 | ArrayList | 提供快速的基于索引的成員訪問,對尾部成員的增加和刪除支持較好 | 成員可為任意Object子類的對象 |
LinkedList | 對列表中任何位置的成員的增加和刪除支持較好,但對基于索引的成員訪問支持性能較差 | 成員可為任意Object子類的對象 | ||
Map | 保存鍵值對成員,基于鍵找值操作,compareTo或compare方法對鍵排序 | HashMap | 能滿足用戶對Map的通用需求 | 鍵成員可為任意Object子類的對象,但如果覆蓋了equals方法,同時注意修改hashCode方法。 |
TreeMap | 支持對鍵有序地遍歷,使用時建議先用HashMap增加和刪除成員,最后從HashMap生成TreeMap;附加實現了SortedMap接口,支持子Map等要求順序的操作 | 鍵成員要求實現caparable接口,或者使用Comparator構造TreeMap。鍵成員一般為同一類型。 | ||
LinkedHashMap | 保留鍵的插入順序,用equals 方法檢查鍵和值的相等性 | 成員可為任意Object子類的對象,但如果覆蓋了equals方法,同時注意修改hashCode方法。 | ||
IdentityHashMap | 使用== 來檢查鍵和值的相等性。 | 成員使用的是嚴格相等 | ||
WeakHashMap | 其行為依賴于垃圾回收線程,沒有絕對理由則少用 |
|
圖2
主要集合接口詳解
Collection 接口
用于表示任何對象或元素組。想要盡可能以常規方式處理一組元素時,就使用這一接口。由Collection接口派生的兩個接口是List和Set
List接口
Java中的List是對數組的有效擴展,它是這樣一種結構,如果不使用泛型,它可以容納任何類型的元素,如果使用泛型,那么它只能容納泛型指定的類型的元素。和數組相比,List的容量是可以動態擴展的。
List中的元素是可以重復的,里面的元素是“有序”的,這里的“有序”,并不是排序的意思,而是說我們可以對某個元素在集合中的位置進行指定。
List中常用的集合對象包括:ArrayList、Vector和LinkedList,其中前兩者是基于數組來進行存儲,后者是基于鏈表進行存儲。其中Vector是線程安全的,其余兩個不是線程安全的。
List中是可以包括null的,即使是使用了泛型。
LinkedList類
LinkedList實現了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在 LinkedList的首部或尾部。這些操作使LinkedList可被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。
ArrayList類
ArrayList實現了可變大小的數組。它允許所有元素,包括null。ArrayList沒有同步。
size,isEmpty,get,set方法運行時間為常數。但是add方法開銷為分攤的常數,添加n個元素需要O(n)的時間。其他的方法運行時間為線性。
每個ArrayList實例都有一個容量(Capacity),即用于存儲元素的數組的大小。這個容量可隨著不斷添加新元素而自動增加,但是增長算法 并沒有定義。當需要插入大量元素時,在插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
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剛創建后是空棧。
Set接口
Set 接口繼承 Collection 接口,而且它不允許集合中存在重復項,每個具體的 Set 實現類依賴添加的對象的 equals()方法來檢查獨一性。Set接口沒有引入新方法,所以Set就是一個Collection,只不過其行為不同。
Set可以大致分為兩類:不排序Set和排序Set,不排序Set包括HashSet和LinkedHashSet,排序Set主要指TreeSet。其中HashSet和LinkedHashSet可以包含null。
Hash表
Hash表是一種數據結構,用來查找對象。Hash表為每個對象計算出一個整數,稱為Hash Code(哈希碼)。Hash表是個鏈接式列表的陣列。每個列表稱為一個buckets(哈希表元)。對象位置的計算 index = HashCode % buckets (HashCode為對象哈希碼,buckets為哈希表元總數)。
當你添加元素時,有時你會遇到已經填充了元素的哈希表元,這種情況稱為Hash Collisions(哈希沖突)。這時,你必須判斷該元素是否已經存在于該哈希表中。
如果哈希碼是合理地隨機分布的,并且哈希表元的數量足夠大,那么哈希沖突的數量就會減少。同時,你也可以通過設定一個初始的哈希表元數量來更好地控制哈 希表的運行。初始哈希表元的數量為 buckets = size * 150% + 1 (size為預期元素的數量)。
如果哈希 表中的元素放得太滿,就必須進行rehashing(再哈希)。再哈希使哈希表元數增倍,并將原有的對象重新導入新的哈希表元中,而原始的哈希表元被刪 除。load factor(加載因子)決定何時要對哈希表進行再哈希。在Java編程語言中,加載因子默認值為0.75,默認哈希表元為101。
HashSet類
在更多情況下,優先使用 HashSet 存儲重復自由的集合。考慮到效率,添加到 HashSet 的對象需要采用恰當分配哈希碼的方式來實現hashCode()方法。雖然大多數系統類覆蓋了 Object中缺省的hashCode()和equals()實現,但創建您自己的要添加到HashSet的類時,別忘了覆蓋 hashCode()和equals()。
TreeSet類
TreeSet是支持排序的一種Set,它的父接口是SortedSet。所以,當您要從集合中以有序的方式插入和抽取元素時,TreeSet實現會有用處。
Map接口
Map接口不是Collection接口的繼承。Map接口用于維護鍵/值對(key/value pairs)。該接口描述了從不重復的鍵到值的映射,和Set類似,Java中的Map也有兩種:排序的和不排序的,不排序的包括HashMap、Hashtable和LinkedHashMap,排序的包括TreeMap。
HashMap類
在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。HashMap不是線程安全的,Hashtable是線程安全的,我們可以把HashMap看做是“簡化”版的Hashtable
無論HashMap還是Hashtable,我們觀察它的構造函數,就會發現它可以有兩個參數:initialCapacity和loadFactor,默認情況下,initialCapacity等于16,loadFactor等于0.75。這和Hash表中可以存放的元素數目有關系,當元素數目超過initialCapacity*loadFactor時,會觸發rehash方法,對hash表進行擴容。如果我們需要向其中插入過多元素,需要適當調整這兩個參數。
TreeMap類
但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好,它不是線程安全的。使用HashMap要求添加的鍵類明確定義了hashCode()和 equals()的實現。這個TreeMap沒有調優選項,因為該樹總處于平衡狀態。
LinkedHashMap類
LinkedHashMap擴展HashMap,以插入順序將關鍵字/值對添加進鏈接哈希映像中。象LinkedHashSet一樣,LinkedHashMap內部也采用雙重鏈接式列表。
WeakHashMap類
WeakHashMap是Map的一個特殊實現,它使用WeakReference(弱引用)來存放哈希表關鍵字。使用這種方式時,當映射的鍵在 WeakHashMap 的外部不再被引用時,垃圾收集器會將它回收,但它將把到達該對象的弱引用納入一個隊列。WeakHashMap的運行將定期檢查該隊列,以便找出新到達的 弱應用。當一個弱引用到達該隊列時,就表示關鍵字不再被任何人使用,并且它已經被收集起來。然后WeakHashMap便刪除相關的映射。
IdentityHashMap類
IdentityHashMap也是Map的一個特殊實現。在這個類中,關鍵字的哈希碼不應該由hashCode()方法來計算,而應該由 System.identityHashCode方法進行計算(即使已經重新定義了hashCode方法)。這是Object.hashCode根據對象 的內存地址來計算哈希碼時使用的方法。另外,為了對各個對象進行比較,IdentityHashMap將使用==,而不使用equals方法。
換句話說,不同的關鍵字對象,即使它們的內容相同,也被視為不同的對象。IdentityHashMap類可以用于實現對象拓撲結構轉換 (topology-PReserving object graph transformations)(比如實現對象的串行化或深度拷貝),在進行轉換時,需要一個“節點表”跟蹤那些已經處理過的對象的引用。即使碰巧有對 象相等,“節點表”也不應視其相等。另一個應用是維護代理對象。比如,調試工具希望在程序調試期間維護每個對象的一個代理對象。
“IdentityHashMap類不是一般意義的Map實現!它的實現有意的違背了Map接口要求通過equals方法比較對象的約定。這個類僅使用在很少發生的需要強調等同性語義的情況。”
參考文章:
http://jianshi-dlw.VEvb.com/blog/1179834
http://blog.csdn.net/zhangerqing/article/details/8122075
http://www.companysz.com/wing011203/archive/2013/05/07/3066021.html
新聞熱點
疑難解答