對HashMap迭代的理解

對程序中有如下代碼
指定HashMap的鍵值對key是字符串,value是整數類型
 HashMap<String ,Integer> maps=new HashMap<String,Integer>();
	     maps.put("name",520);
	     maps.put("age",24);
	     maps.put("nima",55);
	     
	     Set<Map.Entry<String,Integer>> entrySet=maps.entrySet();
//用增強for循環方式迭代取出鍵值對的具體值
         for(Map.Entry<String,Integer> entry:entrySet){
        	 System.out.println(entry.getKey()+":"+entry.getValue());
         }
         

輸出的結果如下

age:24
name:520
nima:55

通過對JDK文檔的瞭解HashMap實現了Map的接口

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

Map集合有一個方法,返回值是Set<Map.Entry<K, V>>

Set<Map.Entry<K, V>> entrySet();
HashMap實現了這個方法,返回值是Set<Map.Entry<K, V>>

public Set<Map.Entry<K,V>> entrySet() {
	return entrySet0();
    }

    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }
Map.Entry<K,V>是一個接口,裏面有方法


接下來說明一下對HashMap 的理解

    要理解HashMap是什麼,首先要明白它的數據結構,在java編程中,最基本的結構就是兩種,一個是數組,另一個是模擬指針(也就是引用),所有的數據結構都可以用這兩個基本結構來構造的,HashMap一樣。HashMap是一個數組和鏈表的結合體,這在數據結構中一般稱爲鏈表散列,如下圖所示


從圖中可以分析道HashMap就是一個數組結構,當新建一個HashMap的時候,就會初始化一個數組,接下來看看java代碼

static class Entry<K,V> inplements Map.Entry<K,V>{

    final K key;

    V value;

   final int hash;

   Entry<K,V> next;

 ......................................

}

上面的entry就是數組中的元素,它持有一個指向下一個元素的引用,就構成了鏈表。

當我們往HashMap中put元素的時候,先根據key和value的值得到這個元素在數組中的位置(下標),然後就額可以把這個元素放在對用的位置。如果這個元素所在的位置已經存放其它元素了,那麼在通一個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭。從HashMap中get元素的時候,首先計算key的hashcode。找到數組中對應的位置的某一個元素,然後通過key的equals方法在對應的位置鏈表找到需要的元素,在這裏可以做一個假設,如果每個位置上的鏈表只有一個元素,那麼HashMap的get效率是最高的。

     Hash算法

    我們可以看到在HashMap中要找到某個元素,需要根據key的hash值來求得對應數組中的位置,如何計算這個位置就是hash算法。前面說過HashMap的數據結構式數組和鏈表的結合,所以我們也非常希望這個HashMap裏面的元素分佈是均勻的,儘量是的每個位置上的元素數量只有一個,那麼當我們用hash算法求得這個位置的時候,馬上就可以知道對應位置的元素就是我們要的,而不用再去遍歷鏈表的全部元素。

    所以我們一開始就可能想到吧HashMap對數組的長度進行取模運算,這樣一來元素的分佈相對來說是比較均勻的。但是“模”運算的消耗還是比較大的。那麼有什麼方法是更加好的呢?

    static int indexFor(int h,int length){

     return h&(length-1);

}

首先算得key的hashcode值。然後跟數組的長度-1做一次與運算。看上去還是很簡單的。但是裏面還是有玄機的。例如數組的長度是2的4次方,那麼hashcode就會和2的4次方-1做與運算。那麼爲什麼HashMap的數組初始化大小都是2的某次方大小時的效率是最高的呢。我們以2的4次方作爲一個例子,來解釋一個爲什麼數組大小爲2的冪時HashMap訪問的性能最高的。

        看如下圖所示,左邊兩組是數組長度爲16也就是2的4次方,右邊兩組是數組長度爲15.兩組的hashcode均爲8和9,但是很明顯,當他們和1110與的時候,產生相同的結果,也就是說他們會定位到數組中的同一個位置上,這就產生了碰撞,8和9被放在同一個鏈表上,那麼查詢的時候就需要遍歷這個鏈表得到8或者9,這樣就降低了查詢效率。同事我們也可以發現,當數組長度爲15的時候hashcode的值會與14(1110)進行與操作,那麼最後一位永遠是0,二0001,0011,0101,1001,1011,0111,1101這幾個位置永遠都不會存放元素了,空間浪費相當大,更糟糕的是在這種情況下,數組可以使用的位置比數組長度小很多,這樣就說明了進一步增加了碰撞的機率,減慢了查詢的效率


所以說當數組長度爲2的N次冪的時候,不同的key算的得到index相同的機率比較小,那麼數據在數組上分佈就比較均勻,也就是說碰撞的機率就比較小,相對而言,查詢的時候就不用遍歷某個位置上的鏈表,這樣查詢效率就會高很多。

 那麼HashMap中默認的數組大小是多少呢,根據查詢可以得知是16,爲什麼是16呢?而不是15或者20呢,那就是上面的說明可以解釋了吧。顯然是因爲16是2的整數次冪的原因。在小數據量的時候16比起15或者20更能減少key之間的碰撞,從而加快查詢效率。









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章