HashMap的hash算法和寻址算法怎么优化的(JDK1.8)

Map 中 put 和 get 的优化

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
举个例子
key="ykx"
key的hashCode是=119718  这是个10进制数
原始hashCode对应二进制11101001110100110==00000000000000011101001110100110
然后我再向右位移16位后的二进制==00000000000000000000000000000001 
异或运算
    0000000000000001 1101001110100110
    0000000000000000 0000000000000001
    0000000000000001 1101001110100111==119719 这个就是最终的hash值

优化点在哪

HashMap在get方法去获取值时是这样的
    hash(key)&(n-1) 进行与运算而找到数组位置
    0000000000000001 1101001110100111 上面优化后的hash值 hashMap默认数组长度16
    0000000000000000 0000000000001111 &
    0000000000000000 0000000000000111 =7
    这就是最终的位置下标  只要数组长度是2的n次方  你会发现:119719%16119719&(16-1)的值时相同的

因为与运算的性能会比取模效率高。

总结

其实这里最重要的几个点就是在算hash值时高是16位与低16进行了异或运算(因为很有可能有两个不同的值hash高16位不相同低16位相同)。这样就导致hashmap寻址运算时低16位已经包含了高16位与低16 的特征,因为在寻址的时候大多数都是低16位在运算,因为数组长度-1的数字大小一般情况都比较小。所以在get寻址时基本都是低16在运算,尽量避免hash冲突,寻址时用与运算代替取模运算也是比较大的优化,只要当hashmap的数组长度是2的n次方那么我们算出来的hash值取模这个长度等于与运算这个长度-1 的值。。

如果说冲突了会怎样

当hash冲突时会在当前下标建立一个链表来维护。jdk为了维护在遍历时性能够好,当链表长度大于8的时候回转化为红黑树,这时查询的时间复杂度就是O(logn)

为什么是8而不是4:我的理解是log2(8)=3而log2(4)=2 由于是红黑树、B+tree等 一般都是3层性能就是折中方案。

HashMap扩容后怎么重新定义位置

默认是16长度,以后每次扩容时 都会重新遍历原来的值拿到hash与 新的数组长度-1 进行与运算放到新的位置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章