jdk7的HashMap實現的思路比較簡單,就是一個Entry數組,數組中每一個Entry都是一個鏈表的起點(表頭)。數組
1 public V put(K key, V value) { 2 if (table == EMPTY_TABLE) { 3 inflateTable(threshold); 4 } 5 //若是key爲null,則將該entry放在第0位 6 if (key == null) 7 return putForNullKey(value); 8 int hash = hash(key); 9 int i = indexFor(hash, table.length); 10 //檢查第i位的鏈表中是否存在該key,若是存在,則將原value替換爲新value 11 for (Entry<K,V> e = table[i]; e != null; e = e.next) { 12 Object k; 13 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 14 V oldValue = e.value; 15 e.value = value; 16 e.recordAccess(this); 17 //並返回舊value 18 return oldValue; 19 } 20 } 21 22 modCount++; 23 //若是不存在則將entry添加在第i位鏈表表頭,若是(size >= threshold) && (null != table[bucketIndex])爲真(threshold=capacity*loadFactor),還會涉及resize(擴容,resize(2 * table.length);)操做 24 addEntry(hash, key, value, i); 25 return null; 26 }
jdk7的hash函數app
1 final int hash(Object k) { 2 int h = hashSeed; 3 if (0 != h && k instanceof String) { 4 return sun.misc.Hashing.stringHash32((String) k); 5 } 6 7 h ^= k.hashCode(); 8 9 // This function ensures that hashCodes that differ only by 10 // constant multiples at each bit position have a bounded 11 // number of collisions (approximately 8 at default load factor). 12 h ^= (h >>> 20) ^ (h >>> 12); 13 return h ^ (h >>> 7) ^ (h >>> 4); 14 }
jdk7中的HashMap存在一個問題,若是key的hash值都映射到同一個桶中,hashMap的查找就會退化成順序查找,這會極大影響查找性能(對插入性能無影響)。jdk8爲了解決這一問題,對HashMap進行了一些改進,當一個桶中的鏈表長度大於內設閾值(8)時,就會將該桶的鏈表樹化(treeify),即將鏈表轉變爲一個紅黑樹(其他位置的鏈表不會受到影響)。函數
1 public V put(K key, V value) { 2 return putVal(hash(key), key, value, false, true); 3 }
jdk8的hash函數性能
1 static final int hash(Object key) { 2 int h; 3 //hashCode返回一個int共32位,最終就是高16位保持原樣,低16位與高16亦或後造成新的低16位。這樣作是爲了防止hashCode低位全0的狀況,在映射後(hash&(table.length-1))就會彙集在第0位。 4 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 5 }
1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 2 boolean evict) { 3 //tab就是table,p是前驅節點,n是table長,i是key的index 4 Node<K,V>[] tab; Node<K,V> p; int n, i; 5 if ((tab = table) == null || (n = tab.length) == 0) 6 n = (tab = resize()).length; 7 //若是第i位尚沒有節點,則直接插入 8 if ((p = tab[i = (n - 1) & hash]) == null) 9 tab[i] = newNode(hash, key, value, null); 10 else {//第i位已經存在節點的狀況 11 Node<K,V> e; K k; 12 //首節點的key與即將插入的key相同 13 if (p.hash == hash && 14 ((k = p.key) == key || (key != null && key.equals(k)))) 15 e = p; 16 //若是首節點是TreeNode,說明該位是一棵紅黑樹,直接插入 17 else if (p instanceof TreeNode) 18 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); 19 //遍歷鏈表進行替換或插入 20 else { 21 for (int binCount = 0; ; ++binCount) { 22 //若是p.next爲null,則插入到鏈表末端 23 if ((e = p.next) == null) { 24 p.next = newNode(hash, key, value, null); 25 //若是鏈表長度大於TREEIFY_THRESHOLD(8,不可設置)則樹化 26 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 27 treeifyBin(tab, hash);//樹化該位的鏈表爲一棵紅黑樹 28 break; 29 } 30 //若是找到相同的key,則直接跳出,後續會更新value 31 if (e.hash == hash && 32 ((k = e.key) == key || (key != null && key.equals(k)))) 33 break; 34 p = e; 35 } 36 } 37 //更新value 38 if (e != null) { // existing mapping for key 39 V oldValue = e.value; 40 if (!onlyIfAbsent || oldValue == null) 41 e.value = value; 42 afterNodeAccess(e); 43 return oldValue; 44 } 45 } 46 ++modCount; 47 if (++size > threshold) 48 resize(); 49 afterNodeInsertion(evict); 50 return null; 51 }