日常开发中位运算不是很常用,但是巧妙的使用位运算可以大量减少运行开销,优化算法。在看jdk源码时,很多地方用到位运算。
位移操作:(只针对 int类型的数据有效,java中,一个int的长度始终是32位,也就是4个字节,它操作的都是该整数的二进制数).也可作用于以下类型,即 byte,short,char,long(它们都是整数形式)。当为这四种类型时,JVM先把它们转换成int型再进行操作。
按位与(&)
按位与的运算规则
- 1&0=0
- 0&0=0
- 1&1=1
- 0&1=0
规则总结:只有两个操作数对应位同为1时,结果为1,其余全为0. (或者是只要有一个操作数为0,结果就为0)。
按位或(|)
按位或的运算规则
- 1|0=1
- 0|0=0
- 1|1=1
- 0|1=1
规则总结:只有两个操作数对应位同为0时,结果为0,其余全为1.(或者是只要有一个操作数为1,结果就为1)。
按位非(~)
按位非的运算规则
把-5转位16位的二进制机器数:11111111 11111111 11111111 11111011
~(-5) 取反结果:00000000 00000000 00000000 00000100
转为十进制,结果为4
规则总结:~ 按位取反操作符,对每个二进制位的内容求反,即1变成0,0变成1。
按位异或(^)
按位异或的运算规则
- 1^1=0
- 1^0=1
- 0^1=1
- 0^0=0
规则总结:^ 异或操作符,相同位值为0 否则为1。
左位移(<<)
m << n的含义:把整数m表示的二进制数左移n位,高位移出n位都舍弃,低位补0. (此时将会出现正数变成负数的可能),如下实例:
5<<2 :把十进制的数值5左移两位,按如下步骤计算
- 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
- 按左移原理,将二进制数左移两位:00000000 00000000 00000000 00010100
- 左移后结果为20
5<<29:把十进制的数值5左移29位,按如下步骤计算
- 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
- 按左移原理,将二进制数左移29位:10100000 00000000 00000000 00000000
- 左移后高位是1,结果显然是负数
小结:m << n即在数字没有溢出的前提下,对于正数和负数,左移n位都相当于m乘以2的n次方。
右位移(>>)
m >>n 的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1(注意:这里计算是使用补码计算,计算机保存也是保存的补码),实例如下:
5>>2 :把十进制的数值5右移两位,按如下步骤计算
- 把5转位16位的二进制机器数:00000000 00000000 00000000 00000101
- 按右移原理,将二进制数左移两位:00000000 00000000 00000000 00000001
- 右移后结果为1
-5>>2:把十进制的数值-5右移两位,按如下步骤计算
- 把-5转位16位的二进制机器数(补码):11111111 11111111 11111111 11111011
- 按右移原理,将二进制数右移两位:11111111 11111111 11111111 11111110
- 右移后结果为-2
小结: m>>n即相当于m除以2的n次方,得到的为整数时,即为结果。
无符号右移(>>>)
m>>>n:整数m表示的二进制右移n位,不论正负数,高位都补0,实例如下:
-5>>>2:把十进制的数值-5右移两位,按如下步骤计算
- 把-5转位16位的二进制机器数:11111111 11111111 11111111 11111011
- 按右移原理,将二进制数右移两位:00111111 11111111 11111111 11111110
- 右移后结果为正数
常见使用
- m*2^n
可以使用m<< n求得结果,如:
System.out.println("2^3=" + (1<<3));//2^3=8
System.out.println("3*2^3=" + (3<<3));//3*2^3=24
计算结果是不是很正确呢?如果非要说2<<-1为什么不等于0.5,前面说过,位运算的操作数只能是整型和字符型。
位移法则
可以发现左移31位和-1位所得的结果是一样的,同理,左移30位和左移-2所得的结果也是一样的。移动一个负数位,是不是等同于右移该负数的绝对值位呢?输出一下就能发现不是的。java中int所能表示的最大数值是31位,加上符号位共32位。在这里可以有这样的位移法则:
1、法则一:任何数左移(右移)32的倍数位等于该数本身。
2、在位移运算m << n的计算中,若n为正数,则实际移动的位数为n%32,若n为负数,则实际移动的位数为(32+n%32),右移,同理。
所以,左移是乘以2的幂,对应着右移则是除以2的幂。
不用临时变量交换两个数
上面的计算主要遵循了一个计算公式:b^(a^b)=a。
其实java中的异或运算法则完全遵守数学中的计算法则:
① a ^ a =0
② a ^ b =b ^ a
③ a ^b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
④ d = a ^b ^ c 可以推出 a = d ^ b ^ c.
⑤ a ^ b ^a = b.