-
public class HashMap<K,V> - extends AbstractMap<K,V>
- implements Map<K,V>, Cloneable, Serializable
基于哈希表的 Map 接口的實現。此實現提供所有可選的映射操作,并允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恒久不變。
此實現假定哈希函數將元素正確分布在各桶之間,可為基本操作(get 和 put)提供穩定的性能。迭代集合視圖所需的時間與 HashMap 實例的“容量”(桶的數量)及其大小(鍵-值映射關系數)的和成比例。所以,如果迭代性能很重要,則不要將初始容量設置得太高(或將加載因子設置得太低)。
HashMap 的實例有兩個參數影響其性能:初始容量 和加載因子。容量 是哈希表中桶的數量,初始容量只是哈希表在創建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,通過調用 rehash 方法將容量翻倍。
通常,默認加載因子 (.75) 在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地降低 rehash 操作次數。如果初始容量大于最大條目數除以加載因子,則不會發生 rehash 操作。
如果很多映射關系要存儲在 HashMap 實例中,則相對于按需執行自動的 rehash 操作以增大表的容量來說,使用足夠大的初始容量創建它將使得映射關系能更有效地存儲。
注意,此實現不是同步的。如果多個線程同時訪問此映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。(結構上的修改是指添加或刪除一個或多個映射關系的操作;僅改變與實例已經包含的鍵關聯的值不是結構上的修改。)這一般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創建時完成這一操作,以防止對映射進行意外的不同步訪問,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
由所有此類的“集合視圖方法”所返回的迭代器都是快速失敗 的:在迭代器創建之后,如果從結構上對映射進行修改,除非通過迭代器自身的 remove 或 add 方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。因此,面對并發的修改,迭代器很快就會完全失敗,而不冒在將來不確定的時間任意發生不確定行為的風險。
注意,迭代器的快速失敗行為不能得到保證,一般來說,存在不同步的并發修改時,不可能作出任何堅決的保證。快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常程序的方式是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用于檢測程序錯誤。
-
public class LinkedHashMap<K,V> - extends HashMap<K,V>
- implements Map<K,V>
Map 接口的哈希表和鏈接列表實現,具有可預知的迭代順序。此實現與 HashMap 的不同之處在于,后者維護著一個運行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序通常就是將鍵插入到映射中的順序(插入順序)。注意,如果在映射中重新插入 鍵,則插入順序不受影響。(如果在調用 m.put(k, v) 前 m.containsKey(k) 返回了 true,則調用時會將鍵 k 重新插入到映射 m 中。)
此實現可以讓客戶避免未指定的、由 HashMap
(及 Hashtable
)所提供的通常為雜亂無章的排序工作,同時無需增加與 TreeMap
相關的成本。使用它可以生成一個與原來順序相同的映射副本,而與原映射的實現無關:
void foo(Map m) {
Map copy = new LinkedHashMap(m);
...
}
如果模塊通過輸入得到一個映射,復制這個映射,然后返回由此副本確定其順序的結果,這種情況下這項技術特別有用。(客戶通常期望返回的內容與其出現的順序相同。)
提供特殊的構造方法
來創建鏈接哈希映射,該哈希映射的迭代順序就是最后訪問其條目的順序,從近期訪問最少到近期訪問最多的順序(訪問順序)。這種映射很適合構建 LRU 緩存。調用 put 或 get 方法將會訪問相應的條目(假定調用完成后它還存在)。putAll 方法以指定映射的條目集合迭代器提供的鍵-值映射關系的順序,為指定映射的每個映射關系生成一個條目訪問。任何其他方法均不生成條目訪問。特別是,collection 視圖上的操作不 影響底層映射的迭代順序。
可以重寫 removeEldestEntry(Map.Entry)
方法來實施策略,以便在將新映射關系添加到映射時自動移除舊的映射關系。
此類提供所有可選的 Map 操作,并且允許 null 元素。與 HashMap 一樣,它可以為基本操作(add、contains 和 remove)提供穩定的性能,假定哈希函數將元素正確分布到桶中。由于增加了維護鏈接列表的開支,其性能很可能比 HashMap 稍遜一籌,不過這一點例外:LinkedHashMap 的 collection 視圖迭代所需時間與映射的大小 成比例。HashMap 迭代時間很可能開支較大,因為它所需要的時間與其容量 成比例。
鏈接的哈希映射具有兩個影響其性能的參數:初始容量和加載因子。它們的定義與 HashMap 極其相似。要注意,為初始容量選擇非常高的值對此類的影響比對 HashMap 要小,因為此類的迭代時間不受容量的影響。
注意,此實現不是同步的。如果多個線程同時訪問鏈接的哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。這一般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創建時完成這一操作,以防止意外的非同步訪問:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
結構修改是指添加或刪除一個或多個映射關系,或者在按訪問順序鏈接的哈希映射中影響迭代順序的任何操作。在按插入順序鏈接的哈希映射中,僅更改與映射中已包含鍵關聯的值不是結構修改。
在按訪問順序鏈接的哈希映射中,僅利用 get 查詢映射不是結構修改。)
Collection(由此類的所有 collection 視圖方法所返回)的 iterator 方法返回的迭代器都是快速失敗 的:在迭代器創建之后,如果從結構上對映射進行修改,除非通過迭代器自身的移除方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。因此,面對并發的修改,迭代器很快就會完全失敗,而不冒將來不確定的時間任意發生不確定行為的風險。
注意,迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步并發修改做出任何硬性保證。快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常的程序的方式是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用于檢測程序錯誤。
此類是 Java Collections Framework 的成員。
回復 更多評論