位运算直接对在内存中的二进制数据进行处理,因此处理数据的速度非常快,巧妙的运用位运算可以使程序更简洁和高效。
C中基本的位运算包含&,|,,^,<<,>>,其中取反()是单目运算,其余为双目运算,java中多了一个无符号右移>>>,最高位的符号位不参与补位。我们知道最高位表示这个数的符号0为正数,1为负数,>>位移会将高位的空缺补成符号位。下图以byte为例
无符号右移.jpg
练习题:
房间有32盏灯,需要实现以下功能:
1.打开某盏灯
2.关闭某盏灯
3.打开所有灯
4.关闭所有灯
5.指定下标返回此灯的状态
要求:使用最小的存储空间。
很多人拿到这个题直接定义一个boolean数组或int数组,但是仔细想想开和关两个状态可以使用一个bit位来保存,int正好32位,所以用1个int就可以处理了。
class Light {
int l; // 用一个int保存32盏灯的状态
}
1、打开某盏灯,即将该位置设1。1或上0,1均为1,所以我们只需将1移到指定的位置然后求或即可。
No.1.jpg
代码如下:
/**
* 打开某盏灯
* @param i 下标1~32
*/
public Light turnOn(int i) {
l |= (1 << i - 1);
return this;
}
2、关闭某盏灯,即将该位置设0。
No.2.jpg
代码如下:
/**
* 关闭某盏灯
* @param i 下标1~32
*/
public Light turnOff(int i) {
l &= ~(1 << i - 1);
return this;
}
3、打开或关闭所有盏灯,即将所有位设1。上面无符号右移的图中可知-1的二进制是全1所以我们可以使用l|-1
,但晕个-1是个魔数无意思可以将-1换成~0
变成l|~0
,还可以使用异或(XOR)相同为0,不同为1,一个数XOR自己将得到0,那XOR自己的反则得到1于是可以写成l^~l
/**
* 打开所有灯
*/
public Light turnOnAll() {
l ^= ~l; // 或 l |= ~0 或 l |= -1
return this;
}
/**
* 关闭所有灯
*/
public Light turnOffAll() {
l ^= l; // 或 l &= 0
return this;
}
5、指定下标返回此灯的状态,这个比较简单把这个位右移到首位然后与1即可
/**
* 返回指定灯的状态
* @param i 下标1~32
*/
public int statusOf(int i) {
return l >>> (i - 1) & 1;
}
下面是完整代码附测试代码
static class Light {
int l; // 用一个int保存32盏灯的状态
/**
* 打开某盏灯
* @param i 下标1~32
*/
public Light turnOn(int i) {
l |= (1 << i - 1);
return this;
}
/**
* 关闭某盏灯
* @param i 下标1~32
*/
public Light turnOff(int i) {
l &= ~(1 << i - 1);
return this;
}
/**
* 打开所有灯
*/
public Light turnOnAll() {
l ^= ~l; // 或 l |= ~0 或 l |= -1
return this;
}
/**
* 关闭所有灯
*/
public Light turnOffAll() {
l ^= l; // 或 l &= 0
return this;
}
/**
* 返回指定灯的状态
* @param i 下标1~32
*/
public int statusOf(int i) {
return l >>> (i - 1) & 1;
}
/**
* 打印,将int转为2进制格式输出
*/
public void println() {
int charPos = 32;
int var = l;
char[] buf = new char[charPos];
for (int i = 0; i < buf.length; i++) {
buf[i] = '0';
}
do {
buf[--charPos] += (var & 1);
var >>>= 1;
} while (var != 0 && charPos > 0);
System.out.println(new String(buf));
}
}
测试代码
public static void main(String[] args) {
// TEST
Light l = new Light();
l.turnOn(1).println();
l.turnOn(10).println();
l.turnOff(1).println();
l.turnOffAll().println();
l.turnOnAll().println();
l.turnOff(32).turnOff(12).println();
System.out.println(l.statusOf(10));
System.out.println(l.statusOf(12));
}
位运算的优先级较低要记得写括号,不然会造成意想不到的后果。