對程序中有如下代碼
指定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之間的碰撞,從而加快查詢效率。