java基础-HashMap
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函数
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 }