1.JDK 1.8中对hash算法和寻址算法如何优化
-
hash算法:hash = (h = key.hashCode()) ^ (h >>> 16)
-
寻址算法:(n - 1) & hash ========= hash对n取模是一样的-----------------------------》定位数组的索引位置
-
与运算(&),取模性能差一些(数组的长度会一直是2的n次方,只要保持数组长度是2的n次方)
-
hash算法没优化前的寻址核心在于低16位之间进行与运算
-
hash算法的优化:对每个hash值,在他的低16位中,让高低16位进行了异或(^),让他的低16位同时保持了高低16位的特征,尽量避免一些hash值后续出现冲突,大家可能会进入数组的同一个位置。
-
寻址算法的优化:用与运算替代取模,提升性能。
2.HashMap如何解决hash碰撞的问题?
-
当发生hash冲突时,会在这个位置挂一个链表,这个链表里面放入多个元素,让多个key-value对,同时放在数组的一个位置里。
-
假设链表很长,可能会导致遍历链表,性能比较差,O(n)
-
优化:如果链表的长度达到了一定的长度(8)之后,会把链表转为红黑树,遍历一遍红黑树找一个元素,O(logn)性能比链表高一些。
3.Hash如何进行扩容?
-
进入扩容有两个情况:
-
添加一个数据,底层数组为空时----》扩容
-
添加一个数据之后,判断当前的数组长度是否大于threshold,大于就进行扩容
-
-
2倍扩容(减少hash冲突),数组变大会进行rehash
-
扩容过程:
-
1、找到新的容量大小和新的threshold大小
-
2、把旧的数据全部复制到新的数组中去
-
--
-
将节点加到链表后
-
容量扩充为原来的两倍,然后对每个节点重新计算哈希值
-
这个值只可能在两个地方:一种是在原下标位置,另一种是在下标为<原下标+原容量>的位置
-