1.7是 数组+链表 1.8是 数组+链表【超过阈值会变成红黑树】
其实一般正常的元素,都是不会超过阈值的,只有插入一堆重复的元素,hash值一样,才可能达到阈值,这个简称Dos攻击 而元素一旦多起来,链表查找的效率就远不及红黑树了
不是的,维护红黑树需要占用比链表更多的空间,而且当链表长度足够短的时候,链表查找的效率反而比红黑树更高??
root、root.left、root.right、root.left.left 有一个为 null ,也会退化为链表(看的是移除之前的情况)
先获得key的hashCode的值 h,然后 h 和 h右移16位 做异或运算。
实质上是把一个数的末x位低16位与他的高16位做异或运算,因为在前面 (n - 1) & hash 的计算中,hash变量只有末x位会参与到运算。使高16位也参与到hash的运算能减少冲突
只有2的n次方,去-1,才能用 & 替代 %
扩容时重新计算索引效率更高: hash & oldCap == 0 的元素留在原来位置 否则新位置 = 旧位置 + oldCap (oldCap:原始的容量)
因为HashMap的初始容量是2的次幂,扩容之后的长度是原来的二倍,新的容量也是2的次幂,所以,元素,要么在原位置,要么在原位置再移动2的次幂。
看下这张图,n为table的长度 图a
表示扩容前的key1和key2两种key确定索引的位置 图b
表示扩容后key1和key2两种key确定索引位置。
元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:
所以在扩容时,只需要看原来的hash值新增的那一位是0还是1就行了【直接 hash & oldCap,就能知道是0还是1了】 是0的话索引没变,是1的话就变成原索引+oldCap
可以的,因为2的n次方也会有缺陷,比如给定的值全是偶数,无论如何hash之后取模,都是偶数,分布就不均匀
此时如果用质数作为容量的话,就会分布得比较均匀
二次 hash 是为了配合 容量是 2 的 n 次幂 这一设计前提,如果 hash 表的容量不是 2 的 n 次幂,则不必二次 hash
容量是 2 的 n 次幂 这一设计计算索引效率更好,但 hash 的分散性就不好,需要二次 hash 来作为补偿,没有采用这一设计的典型例子是 Hashtable
主要是第一个节点才会吧?因为第一个是new Node出来的
jdk1.7中,采用的是头插法,用一个e指针表示当前要扩容的节点,next表示接下来要扩容的节点,一直头插e更新e为next,直到e为null
假设现在有两个线程1和2,要扩容一个Map