在Integer类中有这么一个方法,你能够给它传入一个数字,它将返回小于等于这个数字的一个2的幂次方数。这个方法就是highestOneBit(int i)。java
好比下面的Demo,注意方法的输入与返回值:微信
System.out.println(Integer.highestOneBit(15)); // 输出8
System.out.println(Integer.highestOneBit(16)); // 输出16
System.out.println(Integer.highestOneBit(17)); // 输出16
复制代码
这个方法的实现代码量也是很是少的:分布式
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
复制代码
接下来,咱们就来详细分析一下这块代码的逻辑。微服务
首先,对于这个方法的功能:给定一个数字,找到小于或等于这个数字的一个2的幂次方数。学习
若是咱们要本身来实现的话,咱们须要知道:怎么判断一个数字是2的幂次方数。spa
说真的,我一下想不到什么好方法来判断,惟一能想到的就是一个数字若是把它转换成二进制表示的话,它会有一个规律:若是一个数字是2的幂次方数,那么它对应的二进制表示仅有一个bit位上是1,其余bit位全为0。 好比: 十进制6,二进制表示为:0000 0110 十进制8,二进制表示为:0000 1000 十进制9,二进制表示为:0000 1001 因此,咱们能够利用一个数字的二进制表示来判断这个数字是否是2的幂次方数。关键代码怎么实现呢?去遍历每一个bit位?能够,可是很差,那怎么办?咱们仍是回头仔细看看Integer是如何实现的吧?code
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
复制代码
咱们发现这段代码中没有任何的遍历,只有位运算与一个减法,也就是说它的实现思路和咱们本身的实现思路彻底不同,它的思路就是:给定一个数字,经过一系列的运算,获得一个小于或等于该数字的一个2的幂次方数。cdn
也就是:若是给定一个数字18,经过运算后,要获得16。it
18用二进制表示为: 0001 0010io
想要获得的结果(16)是:0001 0000
那么这个运算的过程无非就是将18对应的二进制数中除最高位的1以外的其余bit位都清零,则拿到了咱们想要的结果。
那怎么经过位运算来实现这个过程呢?
咱们拿18对应的二进制数0001 0010
来举个例子就好了: 先将0001 0010
右移1位, 获得0000 1001
,再与自身进行或运算: 获得0001 1011
。
再将0001 1011
右移2位, 获得0000 0110
,再与自身进行或运算: 获得0001 1111
。
再将0001 1111
右移4位, 获得0000 0001
,再与自身进行或运算: 获得0001 1111
。
再将0001 1111
右移8位, 获得0000 0000
,再与自身进行或运算: 获得0001 1111
。
再将0001 1111
右移16位, 获得0000 0000
,再与自身进行或运算: 获得0001 1111
。
再将0001 1111
无符号右移1位, 获得0000 1111
。
关于无符号右移,能够看我以前写的文章。
最后用0001 1111 - 0000 1111 = 0001 0000
震惊!获得了咱们想要的结果。
其实这个过程能够抽象成这样: 如今有一个二进制数据,0001****
,咱们不关心低位的取值状况,咱们对其进行右移而且进行或运算。
先将0001****
右移1位, 获得00001***
,再与自身进行或运算: 获得00011***
。
再将00011***
右移2位, 获得0000011*
,再与自身进行或运算: 获得0001111*
。
再将0001111*
右移4位, 获得00000001
,再与自身进行或运算: 获得00011111
。
后面不用再推算了,到这里咱们其实能够发现一个规律: 右移与或运算的目的就是想让某个数字的低位都变为1,再用该结果 减去 该结果右移一位后的结果,则至关于清零了原数字的低位。即获得了咱们想要的结果。
到此,只能感叹JDK做者对于位运算的使用已经达到了出神入化的境界了。
若是想学习更多的精彩的Java、分布式、微服务等方面的知识,请关注微信公众号:1点25