什么是位运算
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作; CPU支持位运算,位运算比操作符(+,-,/,*)效率更高。
我们的位运算其实是通过 补码 进行运算的;先把原码转换成补码,对补码进行计算后,在转换成原码
原码,反码,补码
原码: 是最简单的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值。
反码:
- 正数的补码反码是其本身;
- 负数的反码是符号位保持不变,其余位取反。
补码:
- 正数的补码是本身;
- 负数是补码是在它的反码基础上+1
示例: 9 的原码,反码,补码
假如是32位
9 的原码,反码,补码相同:
00000000 00000000 00000000 00001001
示例: -9 的原码,反码,补码
-9的原码: 10000000 00000000 00000000 00001001
-9的反码: 11111111 11111111 11111111 11110110
-9的补码: 11111111 11111111 11111111 11110111
运算符号
符号 | 描述 | 规则 |
---|---|---|
& | 按位与 | 位的两个数字都为1,则为1 |
| | 按位或 | 位只要一个为1即为1。 |
^ | 按位异或 | 位不同则该位为1, 否则该位为0 |
~ | 按位取反 | 按位取反,0变1,1变0。 计算公式:~x=-(x+1); |
<< | 左移 | 各位全部左移n位,低位: 补0 |
>> | 带符号右移 | 各位全部右移n位,如果是正数,高位补0;如果是负数,高位补1 |
>>> | 无符号右移 | 各位全部右移n位,高位无论正负通通补0,低位: 丢弃 |
按位与&
定义:参加运算的两个数据,按二进制位进行“与”运算。
运算规则:
0&0=0 0&1=0 1&0=0 1&1=1
示例
int a = 9; //1001
int b = 12; //1100
System.out.println(a&b);
输出: 8
图解
示例
int a = -9;
int b = 12;
System.out.println(a&b);
输出:4
图解
按位或|
定义:参加运算的两个对象,按二进制位进行“或”运算。
运算规则:
0|0=0 0|1=1 1|0=1 1|1=1
示例
int a = -9;
int b = 12;
System.out.println(a|b);
输出:1
图解
按位异或^
定义:参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:
0^0=0 0^1=1 1^0=1 1^1=0
示例
int a = 9; //1001
int b = 12; //1100
System.out.println(a^b);
输出:5
图解
按位取反~
定义:参加运算的一个数据,按二进制进行“取反”运算。
示例
int a = 9; //1001
System.out.println(~a);
输出: -10
图解
当然: 你可以根据公式:~x=-(x+1)来计算; -(9+1)=-10。
实例
int a = -9; //1001
System.out.println(~a);
输出:8
图解
公式: -( -9+1)= 8
左移<<
各位全部左移n位,低位:补0
实例
int a = 9<<2; //1001
System.out.println(a);
图解
带符号右移>>
运算规则:各位右移n位,符号位为正,高位补零,符号位负高位补一,低位直接移除。
示例
int a = 9>>2; //1001
System.out.println(a);
结果: 2
图解
示例
int a = -9>>2;
System.out.println(a);
输出:-3
图解
无符号右移>>>
无符号右移。无论正数还是负数,高位通通补0.
为什么要使用位运算?
位运算可以提高运算效率,还可以设计巧妙的算法。
如在hashmap就用了挺多位运算
//默认初始化容量 1 << 4 =16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 默认最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//计算hash,让高16为也参与运算
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//寻址算法
p = tab[i = (n - 1) & hash]