匠心零度 转载请注明原创出处,谢谢!
说在前面
看过稍微底层点的源码的人应该都会了解、熟悉里面多多少少会碰到二进制相关操作,因为这个之前还写了一篇java二进制相关基础的基础篇,本篇准备写一些二进制实战技巧相关内容。
主题
- 判断一个数是否是2的幂次方的方法。
- 操作位代表类型。
- 非2的幂次方转换为2的幂次方。
判断一个数是否是2的幂次方的方法
如果该数是无符号整数,可以使用:
private static boolean isPowerOfTwo(int val) {
return (val & (val-1)) == 0;
}
如果一个数是2的n次方,那么这个数用二进制表示时其最高位为1,其余位为0,(val-1)和val都错开了0和1,那么&一定是0。
如果该数是无符号整数,也可以使用:
private static boolean isPowerOfTwo(int val) {
return (val & -val) == val;
}
如果一个数是2的n次方,那么这个数用二进制表示时其最高位为1,其余位为0,(val & -val)就是获取最右1的位,那么如果和val等于就是了。
扩展下,如何判断一个无符号数是2的n次方-1
private static boolean isPowerOfTwoLoseOne(int val) {
return (val & (val+1)) == 0;
}
理由其实和上面类似了。
操作位代表类型
JDK SelectionKey有四种操作类型,分别为:
OP_READ = 1 << 0;
OP_WRITE = 1 << 2;
OP_CONNECT = 1 << 3;
OP_ACCEPT = 1 << 4。
由于只有四种网络操作类型,所以用4 bit就可以表示所有的网络操作位,由于JAVA语言没有bit类型,所以使用了整形来表示,每个操作位代表一种网络操作类型,分别为:0001、0010、0100、1000,这样做的好处是可以非常方便的通过位操作来进行网络操作位的状态判断和状态修改,提升操作性能。
说明:依稀记得RocketMQ里面好像也是类似,有一个当磁盘空间大于90%的时候不给写权限就是类似控制的,后续分析RocketMQ源码的时候会进行说明。
非2的幂次方转换为2的幂次方
在jdk很多集合框架里面,不知道你们还发现了,集合的大小都会是2的幂次方,哈哈,所以就引出了下面的话题。
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
private static final int tableSizeFor(int c) {
int n = c - 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;
}
实例化ConcurrentHashMap时带参数时,会根据参数调整table的大小,确保table的大小总是2的幂次方,调用tableSizeFor的时候每次返回的都是大于等于离该数最近的2的幂次方的数。比如调用tableSizeFor传入c为151的时候 比151大的2的幂次方的数就是256了,核心就是上面的位运算操作。
刚刚上面是求一个数离它最近的大于等于2的幂次方的数,如果求小于等于2的幂次方的数,我们应该怎么办呢?
private static final int tableSizeFor(int n) {
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return n-(n>>1);
}
说明:由于集合的大小都会是2的幂次方,那么求table大小的0.75倍的时候,可以使用(n - (n >>> 2))即可,取模的时候,可以使用hash & 0x7FFFFFFF来进行操作即可。
结束语
上面的都是零度发现的一些实战,如果你发现什么更好的二进制实战,欢迎在留言区积极留言评论。!!!
如果读完觉得有收获的话,欢迎点赞、关注、加公众号【匠心零度】,查阅更多精彩历史!!!