谈谈Java中的位运算
我们为什么要了解使用位运算?
- 某些场景下使用位运算比普通计算使计算机运算效率更高,更节约内存。
- 位运算某些场景比普通方法写出来会更简洁。
- 看很多比较底层的实现源码时,很多已经用了位运算,所以为了更好的了解底层实现,需要懂这相关的知识。
有关位运算的基本概念。
原码,反码,补码的概念及其意义。
二进制机器码即可认为是原码,原码的首位是正负位。
正数的反码与补码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反,负数的补码是在其反码上加1。
反码与补码的意义是为了尽量简单的解决计算机做减法的问题。
位运算的六个运算符号。
& 与运算 两个位都是 1 时,结果才为 1,否则为 0
| 或运算 两个位都是 0 时,结果才为 0,否则为 1
~ 异或运算,两个位相同则为 0,不同则为 1
^ 取反运算,0 则变为 1,1 则变为 0
>> 右移运算,向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位
<< 左移运算,向左进行移位操作,高位丢弃,低位补 0
>>> 无符号右移运算,若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0
常用示例
位操作实现乘除法
数 a 向右移一位,相当于将 a 除以 2;数 a 向左移一位,相当于将 a 乘以 2
1 | int a = 2; |
位操作交货两数
位操作交换两数可以不需要第三个临时变量,虽然普通操作也可以做到,但是没有其效率高
1 | //普通操作 |
位与操作解释:第一步:a ^= b —> a = (a^b);
第二步:b ^= a —> b = b^(a^b) —> b = (b^b)^a = a
第三步:a ^= b —> a = (a^b)^a = (a^a)^b = b
位操作判断奇偶数
只要根据数的最后一位是 0 还是 1 来决定即可,为 0 就是偶数,为 1 就是奇数。
1 | if(0 == (a & 1)) { |
位操作交换符号
交换符号将正数变成负数,负数变成正数
1 | int reversal(int a) { |
整数取反加1,正好变成其对应的负数(补码表示);负数取反加一,则变为其原码,即正数
位操作求绝对值
整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),然后根据符号进行相应的操作
1 | int abs(int a) { |
上面的操作可以进行优化,可以将 i == 0 的条件判断语句去掉。我们都知道符号位 i 只有两种情况,即 i = 0 为正,i = -1 为负。对于任 何数与 0 异或都会保持不变,与 -1 即 0xffffffff 进行异或就相当于对此数进行取反,因此可以将上面三目元算符转换为((a^i)-i),即整数时 a 与 0 异或得到本身,再减去 0,负数时与 0xffffffff 异或将 a 进行取反,然后在加上 1,即减去 i(i =-1)
1 | int abs2(int a) { |