JDK源码阅读笔记--HashMap(tableSizeFor)

tableSizeFor的功能是返回大于输入参数且最近的2的整数次幂的数,比如5则返回8.
java8中的源码是

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;
}

cap-1是为了保证如果cap正好是2的整数次幂,则也可以保证正确的结果。
右移1位保证最高2位是11
再右移2位保证最高4位是1111
再右移4位保证最高8位是11111111
....
最后判断范围并加一正好是2的整数次幂

java12中的方法做了优化,具体哪个版本改的没有调查

static final int tableSizeFor(int cap) {
        int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

numberOfLeadingZeros的返回值是最高位前面0的个数。负数返回0,0返回32。

@HotSpotIntrinsicCandidate
    public static int numberOfLeadingZeros(int i) {
        // HD, Count leading 0's
        if (i <= 0)
            return i == 0 ? 32 : 0;
        int n = 31;
        if (i >= 1 << 16) { n -= 16; i >>>= 16; }
        if (i >= 1 <<  8) { n -=  8; i >>>=  8; }
        if (i >= 1 <<  4) { n -=  4; i >>>=  4; }
        if (i >= 1 <<  2) { n -=  2; i >>>=  2; }
        return n - (i >>> 1);
    }

这个也做了优化,但是还是用折半计算的方法

n=31是因为最后一步移动一位,如果i=1,结果应该是31,则若使n-1>>1位31则n=31,这样减少一步比较(i>=1<<1)即跟0比较。
折半计算每次移动一半的位数,如第一次与1左移16位比较,如果大于则n减16,依此类推。

虽然用了numberOfLeadingZeros,但是位移动的次数没有变化,效率应该差不多。HotSpotIntrinsicCandidate或许可以直接调用CPU指令提高效率。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容