面试官再问你 HashMap 底层原理,就把这篇文章甩给他看 (5)

上边还有一个非常重要的运算,我们没有讲解。就是下边这个判断,它用于把原来的普通链表拆分为两条链表,位置不变或者放在新的位置。

if ((e.hash & oldCap) == 0) {} else {}

我们以原数组容量16为例,扩容之后容量为32。说明下为什么这样计算。

还是用之前的hash值举例。

//e.hash值 0110 1101 0110 1111 0110 1110 0010 1000 //oldCap值,即16 0000 0000 0000 0000 0000 0000 0001 0000 //做与运算,我们会发现结果不是0就是非0, //而且它取决于 e.hash 二进制位的倒数第五位是 0 还是 1, //若倒数第五位为0,则结果为0,若倒数第五位为1,则结果为非0。 //那这个和新数组有什么关系呢? //别着急,我们看下新数组的容量是32,如果求当前hash值在新数组中的下标,则为 // e.hash &( 32 - 1) 这样的运算 ,即 hash 与 31 进行与运算, 0110 1101 0110 1111 0110 1110 0010 1000 & 0000 0000 0000 0000 0000 0000 0001 1111 = 0000 0000 0000 0000 0000 0000 0000 1000 //接下来,我们对比原来的下标计算结果和新的下标结果,看图

看下面的图,我们观察,hash值和旧数组进行与运算的结果 ,跟新数组的与运算结果有什么不同。

面试官再问你 HashMap 底层原理,就把这篇文章甩给他看

会发现一个规律:

若hash值的倒数第五位是0,则新下标与旧下标结果相同,都为 0000 1000

若hash值的倒数第五位是1,则新下标(0001 1000)与旧下标(0000 1000)结果值相差了 16 。

因此,我们就可以根据 (e.hash & oldCap == 0) 这个判断的真假来决定,当前元素应该在原来的位置不变,还是在新的位置(原位置 + 16)。

如果,上边的推理还是不明白的话,我再举个简单的例子。

18%16=2 18%32=18 34%16=2 34%32=2 50%16=2 50%32=18

怎么样,发现规律没,有没有那个感觉了?

计算中的18,34 ,50 其实就相当于 e.hash 值,和新旧数组做取模运算,得到的结果,要么就是原来的位置不变,要么就是原来的位置加上旧数组的长度。

get()方法

有了前面的基础,get方法就比较简单了。

public V get(Object key) { Node<K,V> e; //如果节点为空,则返回null,否则返回节点的value。这也说明,hashMap是支持value为null的。 //因此,我们就明白了,为什么hashMap支持Key和value都为null return (e = getNode(hash(key), key)) == null ? null : e.value; } final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //首先要确保数组不能为空,然后取到当前hash值计算出来的下标位置的第一个元素 if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //若hash值和key都相等,则说明我们要找的就是第一个元素,直接返回 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //如果不是的话,就遍历当前链表(或红黑树) if ((e = first.next) != null) { //如果是红黑树结构,则找到当前key所在的节点位置 if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); //如果是普通链表,则向后遍历查找,直到找到或者遍历到链表末尾为止。 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } //否则,说明没有找到,返回null return null; } 为什么HashMap链表会形成死循环

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspfxx.html