今天在研究Java源码,BitSet.java时看到这样一段代码
// bitIndex 表示要操作的位的索引
public void set(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
// wordIndex()用来求bitIndex索引是存在于哪个数组元素上
int wordIndex = wordIndex(bitIndex);
// 用于判断是否要扩充数组
expandTo(wordIndex)
// words[]用于表示位图,类型为long[]
// 然后奇怪的点就来了,就是这个操作 1L << bitIndex
words[wordIndex] |= (1L << bitIndex); // Restores invariants
checkInvariants();
}
java使用一个long[]
的数组来表示位图,一个long
型元素占8个字节,也就是64bit。当要向位图中进行set
操作的时候会根据参数bitIndex
,求wordIndex
,这个过程是把bitIndex >> 16
相当于是 bitIdex / 64
向下取整,做为words[]
的下标,表示bitIndex
所指的那一位在哪个元素上。那么问题来了,传的参数是int
类型,并且在wordIndex()
中没有对bitIndex
进行赋值操作,所以我们的bitIndex
不会发生变化,那么当bitIndex
的值大于等于64的时候,执行1L << bitIndex
应该会产生溢出,或者全为0 ,但结果并不是这样,来看下面的代码
System.out.println(1 << 0); // 结果 1
System.out.println(1 << 1); // 结果 2
System.out.println(1 << 2); // 结果 4
System.out.println(1 << 31); // 结果 -2147483648
System.out.println(1 << 32); // 结果 1
System.out.println(1 << 33); // 结果 2
System.out.println(1 << 34); // 结果 4
可以看到,当对一个int
类型的数进行左移操作时,假如移动位数超过了该类型定义的总位数,会自动进行取余操作1 << (bitIndex % 64)
,所以在上边的代码中不会出现溢出问题
我们再来看看 >>
操作是不是也是一样的道理
System.out.println(0x40000000 >> 1);
System.out.println(0x40000000 >> 2);
System.out.println(0x40000000 >> 31);
System.out.println(0x40000000 >> 32);
System.out.println(0x40000000 >> 33);
System.out.println(0x40000000 >> 34);
/* 结果同样进行了取余操作 并且我还测试了 >>> 结果是一样的
536870912
268435456
0
1073741824
536870912
268435456
*/