Java | JDK8 | HashMap
从添加一个key~value操作分析起,putVal是一个用final修饰的方法,可知是一个不予许被重写的方法,
传入key的hash值,相对应key~value,
将onlyIfAbsent设置false,意味着当传入的key已经存在时,当前的value会替换掉oldValue,否则只有当value为null时当前value才会替换oldValue
将evict设置为true,目前class中没有具体实现,可以认为没有意义。文档表达:if false, the table is in creation mode.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
分析hash(key)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}通过key.hashCode()计算出key的哈希值,然后将哈希值h右移16位,再与原来的h做异或^运算。
补充:
“>>>”:按二进制形式把所有的数字向右移动对应位数,低位移出(舍弃),高位的空位补零。对于正数来说和带符号右移相同,对于负数来说不同。 其他结构和>>相似。
“^” : 位异或第一个操作数的的第n位于第二个操作数的第n位相反,那么结果的第n为也为1,否则为0 0^0=0, 1^0=1, 0^1=1, 1^1=0
主干流程分析:
1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 2 boolean evict) { 3 Node<K,V>[] tab; Node<K,V> p; int n, i; 4 //step1:如果初次使用hashMap,进行初始化 5 if ((tab = table) == null || (n = tab.length) == 0) 6 n = (tab = resize()).length; 7 //step2:通过hash和(长度-1)做与操作寻址key索引位置-i,当tab[i]没有存值时直接加入,否则做hash冲突处理 8 if ((p = tab[i = (n - 1) & hash]) == null) 9 tab[i] = newNode(hash, key, value, null); 10 else { 11 Node<K,V> e; K k; 12 //step3:判断待添加的key与首元素的key是否相同 13 if (p.hash == hash && 14 ((k = p.key) == key || (key != null && key.equals(k)))) 15 e = p; 16 //step4:判断是否为红黑树结构,若是则进入到另一个方法处理 17 else if (p instanceof TreeNode) 18 //putTreeVal内部进行了遍历,存在相同hash时返回被覆盖的TreeNode,否则返回null。 19 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); 20 //step5:否则是链表结构,执行相关操作,检查是否存在相同的key,否则添加到尾部,并进行检查是否转成红黑树【当链表长度大于等于8进行转换】 21 else { 22 for (int binCount = 0; ; ++binCount) { 23 if ((e = p.next) == null) { 24 p.next = newNode(hash, key, value, null); 25 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 26 treeifyBin(tab, hash); 27 break; 28 } 29 if (e.hash == hash && 30 ((k = e.key) == key || (key != null && key.equals(k)))) 31 break; 32 p = e; 33 } 34 } 35 //step6:对已经有存在相同的key进行操作,返回oldValue 36 //根据onlyIfAbsent,onlyIfAbsent为false时,意味着当传入的key已经存在时,当前的value会替换掉oldValue,否则只有当value为null时当前value才会替换oldValue 37 if (e != null) { // existing mapping for key 38 V oldValue = e.value; 39 if (!onlyIfAbsent || oldValue == null) 40 e.value = value; 41 afterNodeAccess(e); 42 return oldValue; 43 } 44 } 45 //step7:对新增元素操作,判断是否达需要扩容 46 ++modCount; 47 if (++size > threshold) 48 resize(); 49 afterNodeInsertion(evict); 50 return null; 51 }
Q:寻址操作 (n - 1) & hash,为什么是n?
在table数组中寻址,利用了按位与操作的一个特点,1&1 才能为 1,那么也就是说,如果数组最大下标为15,那么不管hash是多少都不会大于15,也就不会数组越界
Q:如何判断是否扩容?
首先看下threshold的初始化
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
//相当于2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//threshold = 初始容量 * 加载因子。是扩容的门槛。相当于实际使用的容量。
int threshold;
//int最大值为2^31,当赋予的初始值比MAXIMUM_CAPACITY大时,这时候再扩容threshold就会造成整型溢出。
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
this.threshold = tableSizeFor(initialCapacity);
//返回一个大于等于且最接近 cap 的2的幂次方整数,如给定9,返回2的4次方16
static final int tableSizeFor(int cap) {
//让cap-1再赋值给n的目的是另找到的目标值大于或等于原值
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
再看扩容操作
1 //当HashMap中存在的node节点大于threshold时,hashmap进行扩容。 2 if (++size > threshold) 3 resize(); 4 5 final Node<K,V>[] resize() { 6 Node<K,V>[] oldTab = table; 7 int oldCap = (oldTab == null) ? 0 : oldTab.length; 8 int oldThr = threshold; 9 int newCap, newThr = 0; 10 if (oldCap > 0) { 11 //当容量预扩容比2^30大时,停止扩容 12 if (oldCap >= MAXIMUM_CAPACITY) { 13 threshold = Integer.MAX_VALUE; 14 return oldTab; 15 } 16 //以自身两倍容量进行扩容 17 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 18 oldCap >= DEFAULT_INITIAL_CAPACITY) 19 newThr = oldThr << 1; // double threshold 20 } 21 else if (oldThr > 0) // initial capacity was placed in threshold 22 newCap = oldThr; 23 else { // zero initial threshold signifies using defaults 24 newCap = DEFAULT_INITIAL_CAPACITY; 25 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 26 } 27 if (newThr == 0) { 28 float ft = (float)newCap * loadFactor; 29 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 30 (int)ft : Integer.MAX_VALUE); 31 } 32 threshold = newThr; 33 @SuppressWarnings({"rawtypes","unchecked"}) 34 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 35 table = newTab; 36 if (oldTab != null) { 37 for (int j = 0; j < oldCap; ++j) { 38 Node<K,V> e; 39 if ((e = oldTab[j]) != null) { 40 oldTab[j] = null; 41 if (e.next == null) 42 newTab[e.hash & (newCap - 1)] = e; 43 else if (e instanceof TreeNode) 44 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 45 else { // preserve order 46 Node<K,V> loHead = null, loTail = null; 47 Node<K,V> hiHead = null, hiTail = null; 48 Node<K,V> next; 49 do { 50 next = e.next; 51 if ((e.hash & oldCap) == 0) { 52 if (loTail == null) 53 loHead = e; 54 else 55 loTail.next = e; 56 loTail = e; 57 } 58 else { 59 if (hiTail == null) 60 hiHead = e; 61 else 62 hiTail.next = e; 63 hiTail = e; 64 } 65 } while ((e = next) != null); 66 if (loTail != null) { 67 loTail.next = null; 68 newTab[j] = loHead; 69 } 70 if (hiTail != null) { 71 hiTail.next = null; 72 newTab[j + oldCap] = hiHead; 73 } 74 } 75 } 76 } 77 } 78 return newTab; 79 }