位运算控制数字范围
通过位运算,可以对一个数进行限制,保证这个数在2n-1(3,7,15,31,63,127,255...)范围内,当大于指定的数时,会取这个0到2n-1里的某个数,不会让它溢出。
之前我写过的位运算的文章
- https://www.cnblogs.com/lori/p/3342781.html
- https://www.cnblogs.com/lori/archive/2013/01/28/2879613.html
- https://www.cnblogs.com/lori/archive/2012/08/10/2631511.html
- https://www.cnblogs.com/lori/archive/2012/06/08/2541399.html
- https://www.cnblogs.com/lori/archive/2012/05/09/2492110.html
本文在ObjectId里的应用
ObjectId最早在mongodb的分布式环境出现,用来生成主键,因为mongodb是一个集群的,分布式的,所以要求它的主键的要求更高;它主要使用一个12个字节进行存储,而我们知道每1个字节对应8位二进制数,而每4二进制数对应1个16进制数,所以12个字节用16进制表示,就是24个16进制字符组成。
位运算与
实现范围限制
在使用&与运算
时,我们知道它对2^-1有一个定理,16进制就是f,ff,fff,ffff,fffff....这些,它对应的二进制是由N个1组成的,它与其它数字进行与时就有一些特性(全1为1,有0则0),通过这个特性,我们可以限定一个数字范围。
@Test
public void bitOperator() {
log.info("10 & 7={}", 10 & 7);
log.info("3 & 7={}", 3 & 7);
log.info("10 & 255={}", 10 & 255);
log.info("300 & 255={}", 300 & 255); //255的16进制为ff,后面数字为2^n-1
}
结果
从图中可以看出,10 & 7
中,10大于后面的7,由于使用了&运算,保证了结果在0~7之间;而3 & 7
,由于3小于7,所以结果就是3本身。
二进制看个究竟
10对应的二进制1010
7 对应的二进制111
现在进行与运算
1010
&
0111
结果
0010
对应10进行是2,由于7是111组成,而10是1010,当位数不同时,需要为111补0,它就变成了0111,这时,高位由于是0,所以不论你的数有多大,结果都是0,最后也就是0到7之间的某个数了,至于是哪个数,这和你的原数有关,原数高位都是0,低位数字不变。
或运算实现从某个数开始计数
对于或运算,也有它的使用场景,我们以二进制说明,比如0x1000这个数(十进制是4096),其它数与它进行或运算,得到的结果将是从4096开始的数字,如果1 | 4096,结果将是4097,以此类推,不过有一点要注意,你的数字在4096范围内,它产生与4096作或运算,产生新的数字是不会重复的;但如果你的数字大于4096,比如4097|4096,它的结果将还是4097,这点需要注意。
如果我们确定了数字范围,可以利用这个性质,在一个较大的数字基础上,去产生新的数字。
begin = Long.parseLong("1000", 16);
for (int i = 0; i < 10; i++) {
log.info("{} | {} = {}", i, begin, i | begin);//超过begin之后,数字会有重复
}
结果