java吧 关注:1,217,928贴子:12,671,042
  • 14回复贴,共1

【求助】ConcurrentHashMap 扩容问题。。。

只看楼主收藏回复

ConcurrentHashMap扩容时,会把旧桶内的所有元素只分成了2份,但是旧key在新table里,不可能只对应2种槽位的,这样新table 按key值hash,就可能找不到,这种情况这么解决。。。


IP属地:上海1楼2023-09-10 19:38回复
    举个例子
    table长度=8
    key=a,在3号槽位
    key=b,在3号槽位
    key=c,在3号槽位
    key=d,在3号槽位
    key=e,在3号槽位
    但是扩容后,长度为16
    key=a,在2号槽位
    key=b,在5号槽位
    key=c,在7号槽位
    key=d,在8号槽位
    key=e,在12号槽位
    这种情况这么解决,jdk只把所有key只分成2种槽位,但是不可能所有key对应新长度,只存在2种槽位的,这个要这么解决。。。


    IP属地:上海2楼2023-09-10 20:00
    收起回复
      没看懂


      IP属地:广东来自Android客户端3楼2023-09-10 20:10
      回复
        扩容之后就去找扩容之后的数组和红黑树啊,为什么会找不到,放数据的那块你应该是没看明白它的逻辑


        IP属地:贵州来自Android客户端4楼2023-09-10 20:35
        回复
          “不可能只对应2种槽位”。事实上就是只有两种。对象的hash值是固定的。然后存的时候是直接用(哈希数组大小-1)与对象的hash值做与运算。下面是过程(很久没写字了,自己分辨)


          IP属地:广东来自Android客户端5楼2023-09-10 20:56
          收起回复
            哈希取余后只有两种可能啊,2%3 =2,8%3=2,11%3=2,14%3=2扩容后变成2%6=2,8%6=2,11%6=5,14%6=2。够直观了么,扩容又不是扩了之后按顺序填进去。可以去看看取余是怎么计算的


            IP属地:福建来自Android客户端7楼2023-09-11 02:20
            回复
              没看过concurrent hashmap的源码,只说一下hashmap的源码。首先是如何计算散列值,对于一个key,设key.hashcode=h,散列值为h^(h>>>16);而对于给定的散列值,如何确定应该存放在哪个bucket中,hashmap采用这样的计算方式:(n-1)&hash(其实就是除余),其中n是hashmap的容量。接下来就是hashmap容量如何计算,hashmap会保证自己的容量是2的整数次幂,哪怕你初始化的时候指定了其他值,hashmap仍然会计算出比你指定的数字大但最接近你指定数字的2的整数次幂。例如,你指定容量为26,实际创建的hashmap会创建一个大小为32的数组存储元素。hashmap设定自己的负载因子为0.75,当放入一个新元素的时候如果使得负载因子超过界限就会扩容,扩容的时候,新容量是旧容量的两倍(暂时不考虑上限情况),然后会把旧数组中的元素搬入新数组。这就能解释为什么旧bucket中的元素被分成两份后一定对应两种槽位,因为n是2的整数次幂,n-1在二进制下就是从低位到高位的连续的若干个1,假设旧的n是32,32-1就是0011111(省略部分前导0),某个hash值与其做与运算后,一定是一个小于32的数字,扩容后64-1=63,二进制表示0111111,只有第6位多了一个1那么原来同一个bucket下的元素就被分成两部分:第6位是1,第6位不是1。不是1的仍然位于旧bucket,是1的被放入新bucket。所以你在二楼写的那种情况是不存在的,旧bucket的元素在扩容后被散列到的位置只能二选一。


              IP属地:江苏来自Android客户端8楼2023-09-11 09:16
              回复
                首先你要知道key在数组中的位置是根据(数组长度 - 1) & spread(key.hashCode()))得到的
                ,数组长度我记得默认是16,16-1变成二进制就是0000 1111,扩容都是扩容两倍,也就是32,那么32-1就是 0001 1111,和16-1一比是不是就是在第五位上多了个1呢,然后在看下图的我标志的if方法,这里的n是扩容前的数组长度也就是16,16是0001 0000,然后这个if是 & 哦,也就是说如果key的hash值的第五位有1就为1,否则就为0

                因为数组是连续的,所以说当数组变大的时候,可以有部分数据不进行迁移,而是第五位有值的进行迁移。
                并不是你说的只分成两份


                IP属地:重庆9楼2023-09-11 09:24
                回复
                  扩容后元素到新数组下标只有两种情况,要么新下标等于旧下标,要么等于旧下标+旧数组length


                  IP属地:广东来自iPhone客户端10楼2023-09-13 11:26
                  回复