HashMap-----tableSizeFor()

本文深入探讨了HashMap如何确保其容量为2的幂次方。详细分析了tableSizeFor方法的实现原理,通过位操作高效地计算出大于等于指定容量的最小2的幂次方。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 问题引入

  我们在学习HashMap时会发现HashMap的容量必须是2的幂次方,那么当我们在创建HashMap对象时,如果指定一个容量且其不为2的幂次方,HashMap是如何实现其容量为2的幂次方?
HashMap()构造方法

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

  我们可以看到,当HashMap实例化对象是,如果给定了initCapactiy,由于HashMap的容量capacity都是2的幂次方,因此这个方法用于找到大于等于initalCapacity的最小的2的幂次方(如果initalCapacity是2的幂次方,则返回的还是这个数)。接下来我们将去分析tableSizeFor()方法。

2. tableSizeFor()源码剖析

功能: 返回一个大于等于输入参数的最小2的幂次方(不考虑大于最大容量的情况)。
源码:

    /**
     * Returns a power of two size for the given target capacity.
     * 返回一个大于等于给定目标容量的最小2的幂次方
     */
    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;
    }
    
    /**
     * The maximum capacity, used if a higher value is implicitly specified
     * 最大容量,如果传入参数指定更大的值,则使用该容量
     * by either of the constructors with arguments.
     * 由具有参数的任意构造函数
     * MUST be a power of two <= 1<<30.
     * 容量必须是2的幂次方,且小于等于2^30
     */
    static final int MAXIMUM_CAPACITY = 1 << 30; 

  首先,通过源码的注释我们可以简单的了解到tableSizeFor()的功能:
该方法是如果给定目标容量小于最大容量时,返回一个大于等于给定目标容量的最小的2次方幂;否则,返回最大容量。

算法分析

注意:
  我们通过上面HashMap的构造方法可以知道,cap是一个大于等于0,小于等于最大容量的数。

  首先,我们可以看到我们指定的参数一进来就会对cap作减1操作。int n = cap - 1;这个问题先保留,我们到下面进行解说。
 在进行之前会有一个问题,一开始进行进入方法,执行cap-1操作,如果cap为1,会使n为0,这时经过后面的几次无符号右移依然是0,最后n+1操作会返回1。所以我们下面先讨论n不等于0的情况。
第一次右移

n |= n >>> 1;

  由于n不等于0,则n(00001xxxxxx)的二进制表示中总会有一个bit位为1,这是考虑最高位的1。通过无符号右移1位,则将最高位的1右移了1位,再做或运算,使得二进制表示中与最高位的1紧邻的右边一位。如:000011xxxxxx。注:这里的x只是占位表示没有确定个数
在这里插入图片描述
第二次右移

n |= n >>> 2;

  在上面我们已经进行了一次右移操作n |= n >>> 1。假设此时n为,则无符号右移两位会将最高位两个连续的1右移两位,然后再与原来的n做或运算,这样n的二进制表示的最高位中会有4个连续的1。如:00001111xxxxxx。
在这里插入图片描述
第三次右移

n |= n >>> 4;

这是高位中已经有4个1的数再右移4位,在做或运算,这样n的二进制表示的高位中会有8个连续的1。如:00001111 1111xxxxxx
在这里插入图片描述以此类推
右移1位做或运算后,最高位有2个1;右移2位做或运算后,最高位有4个1;右移4位做或运算后,最高位有8个1。我们就可以找到其中的规律,需注意的是:n为int型数,容量最大为32bit的整数,因此最后n |= n >>> 16;,最多也就32个1,但是这时候已经大于MAXIMUM_CAPACITY,所以取值到MAXIMUM_CAPACITY
  看了这个之后,我们再来举个例子解释一下。
举例: 当cap为10时,返回结果为?
在这里插入图片描述我们从图上可以分析出来,当cap为10时,返回的结果为16。现在我们通过分析可以知道了为什么要在一开始的时候减1,为了预防cap刚好是2的幂次方,则返回的值不为大于等于cap的最小的2的幂次方。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值