首先我们先简单的了解一下位运算,众所周知,我们的数据是以二进制的形式储存在我们的计算机中的。位运算是计算机的核心基础,数据的表示和计算几乎都少不了,在JVM以及很多高性能代码里大量使用,甚至很多算法本身就是基于位进行的。在算法方面,很多位相关的算法有很多技巧,不学真不知道。另外很多算法虽然看起来与位运算无关,但是用位操作优化一下,性能会提升很多,所以位运算的问题值得好好学习。
与、或、异或和取反
与的运算符是&,运算规则是: 对于每个二进制位,当两个数对应的位都为 1 时,结果才为 1,否则结果为 0。
- 0&0=0
- 1&0=0
- 0&1=0
- 1&1=1
或的符号是|,运算规则是: 对于每个二进制位,当两个数对应的位都为 0 时,结果才为 0,否则结果为 1。
- 0|0=0
- 1|0=1
- 0|1=1
- 1|1=1
异或运算的符号⊕(在代码中用^表示异或),运算规则是: 对于每个二进制位,当两个数对应的位相同时,结果为 0,否则结果为 1。
- 0⊕0=0
- 1⊕0=1
- 0⊕1=1
- 1⊕1=0
取反运算的符号是~,运算规则是: 对一个数的每个二进制位进行取反操作,0 变成1,1 变成 0。
- ~0=1
- ~1=0
以下例子显示上述四种位运算符的运算结果,参与运算的数字都采用有符号的 8 位二进制表示46 的二进制表示是 00101110,51 的二进制表示是 00110011。考虑以下位运算的结果。。46&51的结果是34,对应的二进制表示是 00100010。
46|51 的结果是63,对应的二进制表示是 00111111。
。46田51的结果是29,对应的二进制表示是 00011101.
。~46 的结果是-47,对应的二进制表示是 11010001。
。~51 的结果是 -52,对应的二进制表示是 11001100。
位运算常用技巧
幂等律: a &a=a,a a=a (注意异或不满足幂等律) ;
交换律: a & b=b &a,al b=b a,a⊕b=b⊕a;
结合律: (a & b) & c=a &(b & c),(alb) c=a(b c),(ab)⊕c=a⊕(b⊕c);分配律: (a&b)l c=(a c &(b c, (al b) & c=(& c)b &c,ab) & c=(a& cb &c);
德摩根律: ~(a & b)=(~a) l (~b),~(a | b)=(~a) & (~b);
取反运算性质: -1=~0,-a=~(a-1);
与运算性质: a & 0=0,a &(-1)=a,a & (~a)=0;
或运算性质: a =a;
异或运算性质: a⊕0=a,a⊕a=0;
获取某一位数的代码
boolean getBit(int num,int i){
//检查该结果是否为零。不为零说第i位为1
return (num&(1<<i)!=0);
}
设置某一位为1
int setBit(int num,int i){
return num|(1<<i);
}
清零(将某一位设置为0)
int clearBit(int num,int i){
int mask = ~(1<<i);
return num&mask;
}
更新(将清零和设置结合起来)
int update(int num,int i ,int v){
int mask = ~(1<<i);
return (num&mask)|(v<<i);
}