http://tangmingjie2009.iteye.com/blog/1698595
http://blog.csdn.net/itm_hadf/article/details/7497462
ava.util 類 HashMap<K,V>
java.lang.Object
java.util.AbstractMap<K,V>
java.util.HashMap<K,V>
型別參數:
K - 此映射所維護的鍵的類型
V - 所映射值的類型
所有已實現的介面:
Serializable, Cloneable, Map<K,V>
直接已知子類:
LinkedHashMap, PrinterStateReasons
--------------------------------------------------------------------------------
public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable基於雜湊表的 Map 介面的實現。
特點:
(1)此實現提供所有可選的映射操作,並允許使用 null 值和 null 鍵。
(除了非同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)
(2)此類不保證映射的順序,特別是它不保證該順序恒久不變。
(3)此實現假定雜湊函數將元素適當地分布在各桶之間,可為基本操作(get 和 put)提供穩定的效能。
迭代 collection 視圖所需的時間與 HashMap 執行個體的“容量”(桶的數量)及其大小(鍵-值對應關係數)成比例。
所以,如果迭代效能很重要,則不要將初始容量設定得太高(或將載入因子設定得太低)。
HashMap 的執行個體有兩個參數影響其效能:初始容量 和載入因子。
(1)容量 是雜湊表中桶的數量,初始容量只是雜湊表在建立時的容量。
(2)載入因子 是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。
當雜湊表中的條目數超出了載入因子與當前容量的乘積時,則要對該雜湊表進行 rehash 操作(即重建內部資料結構),從而雜湊表將具有大約兩倍的桶數。
通常,預設載入因子 (.75) 在時間和空間成本上尋求一種折衷。
載入因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。
在設定初始容量時應該考慮到映射中所需的條目數及其載入因子,以便最大限度地減少 rehash 操作次數。
如果初始容量大於最大條目數除以載入因子,則不會發生 rehash 操作。
如果很多映射關係要儲存在 HashMap 執行個體中,則相對於按需執行自動的 rehash 操作以增大表的容量來說,使用足夠大的初始容量建立它將使得映射關係能更有效地儲存。
注意,此實現不是同步的。如果多個線程同時訪問一個雜湊映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。
(結構上的修改是指添加或刪除一個或多個映射關係的任何操作;僅改變與執行個體已經包含的鍵關聯的值不是結構上的修改。)
這一般通過對自然封裝該映射的對象進行同步操作來完成。
如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“封裝”該映射。
最好在建立時完成這一操作,以防止對映射進行意外的非同步訪問,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
由所有此類的“collection 視圖方法”所返回的迭代器都是快速失敗 的:
在迭代器建立之後,如果從結構上對映射進行修改,除非通過迭代器本身的 remove 方法,
其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。
因此,面對並發的修改,迭代器很快就會完全失敗,而不冒在將來不確定的時間發生任意不確定行為的風險。
注意,迭代器的快速失敗行為不能得到保證,一般來說,存在非同步的並發修改時,不可能作出任何堅決的保證。
快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。
因此,編寫依賴於此異常的程式的做法是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用於檢測程式錯誤。
此類是 Java Collections Framework 的成員。
(1)
containsKey
public boolean containsKey(Object key)如果此映射包含對於指定鍵的映射關係,則返回 true。
指定者:
介面 Map<K,V> 中的 containsKey
覆蓋:
類 AbstractMap<K,V> 中的 containsKey
參數:
key - 要測試其是否在此映射中存在的鍵
返回:
如果此映射包含對於指定鍵的映射關係,則返回 true。
(2)
keySet
public Set<K> keySet()返回此映射中所包含的鍵的 Set 視圖。該 set 受映射的支援,所以對映射的更改將反映在該 set 中,反之亦然。如果在對 set 進行迭代的同時修改了映射(通過迭代器自己的 remove 操作除外),則迭代結果是不確定的。該 set 支援元素的移除,通過 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作可從該映射中移除相應的映射關係。它不支援 add 或 addAll 操作。
指定者:
介面 Map<K,V> 中的 keySet
覆蓋:
類 AbstractMap<K,V> 中的 keySet
返回:
此映射中包含的鍵的 set 視圖【有關 set 使用 見下文。】
下面借鑒一個執行個體示範解析:
[java] view plain copy print ? public class HashMapTest { public static void main(String[] args) { HashMap<String,String> keySetMap = new HashMap<String,String>(); HashMap<String,String> entrySetMap=new HashMap<String,String>(); for (int i= 0;i<1000;i++) { keySetMap.put(""+i, "keySet"); } for(int i=0;i<1000;i++){ entrySetMap.put(""+i,"entrySet"); } long startTimeOne = System.currentTimeMillis(); <strong><span style="color:#ff0000;">Iterator<String> keySetIterator = keySetMap.keySet().iterator(); while (keySetIterator.hasNext()) { System.out.println(</span><span style="color:#000099;">keySetMap.get(keySetIterator.next())</span><span style="color:#ff0000;">); }</span></strong> System.out.println("keyset遍曆時間-------------------------------:"+(System.currentTimeMillis()-startTimeOne)); long startTimeTwo=System.currentTimeMillis(); Iterator<Entry<String,String>> entrySetIterator=entrySetMap.entrySet().iterator(); while(entrySetIterator.hasNext()){ Entry<String,String> entry=entrySetIterator.next(); System.out.println(entry.getValue()); } System.out.println("entryset遍曆時間---------------------------:"+(System.currentTimeMillis()-startTimeTwo)); } }
通過多次運行測試發現,entryset遍曆時間比keyset遍曆時間短許多,entryset方式的效能通常要比keyset方式高一倍。
三。原因何在。
通過查看原始碼發現,調用keySetMap.keySet()這個方法會產生keyIterator迭代器,
其next()方法只返回其key值,然後再通過key值在keySetMap中獲得其value值,代碼如:keySetMap.get(keySetIterator.next())
而調用entrySetMap.entrySet()方法會產生EntryIterator迭代器,其next()方法返回一個Entry對象的一個執行個體,其中包含key值和value值。
如果遍曆HashMap時只取其key值,那麼兩種方式的遍曆在效能上應該是相同的。
但同時取key值和value值時,keyset方式比entryset方式多遍曆了一次table,此時keyset方式效能差些。
hashMap深度分析(轉載)
java.util.HashMap是很常見的類,前段時間公司系統由於對HashMap使用不當,導致cpu百分之百,
在並發環境下使用HashMap 而沒有做同步,可能會引起死迴圈,關於這一點,sun的官方網站上已有闡述,這並非是bug。
HashMap的資料結構
HashMap主要是用數組來儲存資料的,我們都知道它會對key進行雜湊運算,哈系運算會有重複的雜湊值,對於雜湊值的衝突,HashMap採用鏈表來解決的。
在HashMap裡有這樣的一句屬性聲明:
transient Entry[] table;
Entry就是HashMap儲存資料所用的類,它擁有的屬性如下
final K key;
V value;
final int hash;
Entry<K,V> next;
看到next了嗎。next就是為了雜湊衝突而存在的。比如通過雜湊運算,一個新元素應該在數組的第10個位置,但是第10個位置已經有Entry,那麼好吧,將新加的元素也放到第10個位置,將第10個位置的原有Entry賦值給當前新加的 Entry的next屬性。數組儲存的是鏈表,鏈表是為瞭解決雜湊衝突的,這一點要注意。
幾個關鍵的屬性
儲存資料的數組
transient Entry[] table; 這個上面已經講到了
預設容量
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
預設載入因子,載入因子是一個比例,當HashMap的資料大小>=容量*載入因子時,HashMap會將容量擴容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
當實際資料大小超過threshold時,HashMap會將容量擴容,threshold=容量*載入因子
int threshold;
載入因子
final float loadFactor;
HashMap的初始過程
建構函式1:
[java] view plain copy print ?