Java筆記四.HashMap解析__Java

來源:互聯網
上載者:User

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 ?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.