HashMap与加载因子/负载因子loadFactor关系
HashMap以<key,value>的方式存放數(shù)據(jù),存儲(chǔ)在數(shù)組中。通過(guò)開(kāi)散列方法解決沖突,數(shù)組中存放的Entry作為單向鏈表的表頭。
Entry的源碼如下:
static class Entry<K,V> implements Map.Entry<K,V> {final K key;V value;Entry<K,V> next;final int hash;//構(gòu)造、get、set等方法省略public final boolean equals(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry e = (Map.Entry)o;Object k1 = getKey();Object k2 = e.getKey();if (k1 == k2 || (k1 != null && k1.equals(k2))) {Object v1 = getValue();Object v2 = e.getValue();if (v1 == v2 || (v1 != null && v1.equals(v2)))return true;}return false;}public final int hashCode() {return (key==null ? 0 : key.hashCode()) ^(value==null ? 0 : value.hashCode());}}下面我們通過(guò)走讀源碼來(lái)逐步探索HashMap的奧秘!!!
一)“發(fā)現(xiàn)問(wèn)題,存有疑問(wèn)!”
1)構(gòu)造HashMap中的capacity
capacity為當(dāng)前HashMap的Entry數(shù)組的大小,為什么Entry數(shù)組的大小是2的N次方?
// Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity)capacity <<= 1;2)構(gòu)造HashMap中的loadFactor(裝填因子)
threshold = (int)(capacity * loadFactor);threshold為HashMap的size最大值,注意不是HashMap內(nèi)部數(shù)組的大小。
為什么需要loadFactor,怎么合理的設(shè)置loadFactor?
3)HashMap的put
public V put(K key, V value) {if (key == null)return putForNullKey(value);//當(dāng)key為null時(shí),將其存放在數(shù)組的首部:table[0]int hash = hash(key.hashCode());int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null; }這里我們關(guān)注兩個(gè)地方:
1)hash
static int hash(int h) {// This function ensures that hashCodes that differ only by// constant multiples at each bit position have a bounded// number of collisions (approximately 8 at default load factor).h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}hash的作用是什么?
2)indexFor
static int indexFor(int h,int length) {return h & (length-1);}indexFor的作用是什么?
二)“解惑,撥云見(jiàn)日!”
key的hashCode經(jīng)過(guò)hash后,為了讓其在table(table為hashMap的entry[])的范圍內(nèi),需要再hash一次。這里實(shí)際上是采用的“除余法”(h%length)。
源于一個(gè)數(shù)學(xué)規(guī)律,就是如果length是2的N次方,那么數(shù)h對(duì)length的模運(yùn)算結(jié)果等價(jià)于a和(length-1)的按位與運(yùn)算,也就是?h%length?<=> h&(length-1)。
位運(yùn)算當(dāng)然比取余效率高,因此這就解釋了:為什么Entry數(shù)組的大小是2的N次方?
我們?yōu)槭裁床恢苯訉?duì)key的hashCode進(jìn)行indexFor運(yùn)算,還要再hash一次呢?
我做了個(gè)簡(jiǎn)單實(shí)驗(yàn),如下:
int[] hashcode = new int[] { 100000001,100000011,100000111,100001111,100011111,100111111,101111111,111111111 }; for (int c : hashcode)System.out.println(hash(c));數(shù)組hashcode中假定了8個(gè)hashcode,若對(duì)他們除以10取余,余數(shù)都為1,全部沖突!
但是通過(guò)hash后,對(duì)應(yīng)的值為:
94441116; 94441110; 94441204; 94440071; 94558923; 94592891; 107664244; 117165177
對(duì)上面的值除以10取余分別是:6、0、4、1、3、1、4、7,沖突為2。
可見(jiàn)hash能夠?qū)ashCode分布更均勻,防止一些蹩腳的hash函數(shù)!
從上面我們也可以看出取余的時(shí)候,高位的影響比較小,例如:1048592、1048832、1052672、1114112、2097152都可以被16整除(余數(shù)為0)!
那么我們得想個(gè)辦法讓高位也要影響到取余的結(jié)果,如是便有了Hash。
詳情參考:http://marystone.iteye.com/blog/709945
對(duì)于loadFactor,我也做了個(gè)簡(jiǎn)單實(shí)驗(yàn),如下:
public static void main(String[] args) {String s = "a aa aaa b bb bbb c cc ccc d dd ddd e ee eee f ff fff g gg ggg h hh hhh abc bcd cde def efg fgh ghi hij ijk ";String[] ss = s.split(" ");int size = ss.length;//Set<Integer> indexS = new HashSet<Integer>();int conflict = 0;for (int i = 0; i < ss.length; i++) {int index = hash(ss[i].hashCode()) % size;if (indexS.contains(index))conflict++;elseindexS.add(index);}System.out.println("沖突數(shù):"+conflict);}當(dāng)參與取余的除數(shù)為size時(shí),沖突數(shù)為13
當(dāng)參與取余的除數(shù)為2*size時(shí),沖突數(shù)為9
當(dāng)參與取余的除數(shù)為3*size時(shí),沖突數(shù)為6
在hashMap中,size受loadFactor的影響。
極端的想,
如果loadFactor很小很小,那么map中的table需要不斷的擴(kuò)容,導(dǎo)致除數(shù)越來(lái)越大,沖突越來(lái)越小!
如果loadFactor很大很大,那么當(dāng)map中table放滿了也不要求擴(kuò)容,導(dǎo)致沖突越來(lái)越多,解決沖突而起的鏈表越來(lái)越長(zhǎng)!
原文鏈接:http://www.cnblogs.com/huangfox/archive/2012/07/06/2579614.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的HashMap与加载因子/负载因子loadFactor关系的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: HashMap中提到的散列是什么?
- 下一篇: 几种排序算法的思想