[java基础-hashMap] capacity/算槽位等有意思的操作

精妙的位移运算

初始化槽位大小,返回比cap大的最小2的N次方

static final int tableSizeFor(int cap) {
        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;
    }

2的次方数特点 只有一个bit位是1 其余都是0    eg : 0000 1000 -> 8

以9为例走一遍 9变16====================================开始

1. 9的二进制表示  0000 0000 0000 1001 

2. 减1后 ,n = 0000 1000 (高位都是0 忽略掉)

3. 右移一位  0000 0100

4.或运算       0000 1000  >> 0000 1100

5.右移两位    0000 0011

6.或运算        0000 1100 >> 0000 1111

7.右移四位     0000 0000

8.或运算         0000 1111 >> 0000 1111

9.右移八位     0000 0000

10. 跳过一些 直接到右移动16位或运算  n此时低四位都是1

11.n+1  0001 0000 => 16

9变16====================================结束

走了这么多,总结下就是把n-1的二进制的 高位是1开始后面的值全部设置成0(含第一个高位是1的那位),高位往前一位设置成1

减1是为了兼容本身就是2的N次方的数

看下5到8 9到16可以清晰看出来
5   0000 0101
8   0000 1000
9   0000 1001
16  0001 0000

 

原始的计算槽位

(n-1)&  hash        n是数组的长度 (2的次方数 ), hash是key的经过处理的hash(处理看下面那一小段)

这个与运算比较简单了 容量是16位的数组 n-1 就是 0000 0000 0000 1111 与出来的值最多是15.管你是啥 高位全部归零 

简单说下对这个hash的处理

(h=key.hashCode()) ^ (h>>>16)

h右移16位 主要让高位右移 减少hash碰撞[想下 如果低位都是0 那效果估计很糟糕 疯狂碰撞]

 

rehash计算槽位

newTab[e.hash & (newCap - 1)] = e;

具体自己画图领悟吧,说下结论 扩容是oldCap<<1 即容量扩大了一倍

老元素的key.hash&操作后 ,两种情况【自己要看原来的hash在扩容的哪一位上是不是1】

1.老的key的低N位如果是0那还是在老的槽位,

2.低N位如果是1  新坐标= 老坐标 + OldCap

举个通俗的例子 默认大小是16个槽位,扩容后变成32个槽位

假设x的hash是(0000 0010)在老数组的2个位置,扩容散列后 它不受影响还是在第二个位置 (他没有潜力 他的高位全是0)

y的hash是 0001 1111 原来他的index是15 扩容后它到了31个

                  0000 1111 (这个是16位的cap-1)

                  0001 1111 (这个是32位的cap-1)

 

扩容机制

前置

hashMap的所有元素量>=扩容阀值&数组位置已经有值 1.7的逻辑,1.8里面把第二个条件移除

具体骚操作

jdk1.7 主要是正常插入的时候是尾插,rehash的时候是头插

jdk1.0 都是尾插,具体代码如下 用了两个链表

Node<String,String> loHead = null, loTail = null;
        Node<String,String> hiHead = null, hiTail = null;
        Node<String,String> next;
        do {
            next = e.next;
            System.out.println("hash-" + e.hash + "& oldCap" + (e.hash & oldCap));
            System.out.println(oldCap);
//            1111001100000001110101110000
//            0000000000000000000000001000
            //低位是0的 用loHead装配
            if ((e.hash & oldCap) == 0) {
                if (loTail == null)
                    loHead = e;
                else
                    loTail.next = e;
                loTail = e;
            }
            else {
                if (hiTail == null)
                    hiHead = e;
                else
                    hiTail.next = e;
                hiTail = e;
            }
        } while ((e = next) != null);
        if (loTail != null) {
            loTail.next = null;
            newTab[j] = loHead;
        }
        if (hiTail != null) {
            hiTail.next = null;
            newTab[j + oldCap] = hiHead;
        }

 

fail-fast细节

for循环会检测modCount和exceptedModCount,每次变更都会增加modCount 不一致就会报错

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值