通常我们在程序里面会保存很多的开关,那么每个开关就要占一个字段
一个int是4个字节,一个字节有8位,那么int有32个01代码二进制,去掉int类型的最左边的一个01代码,这个01代码使用来表示正负号的,那么去掉这个01代码之后,还剩下31个01代码。那么这31个01代码就可以设置成31个开关,0表示false,1表示true。
我们先来看一个简单的例子,linux系统里面的权限用1,2,4来表示执行,可读,可写;三种权限。
我们先列出1,2,4的二进制
1:00000001
2:00000010
4:00000100
一个可读可写可执行的权限就相当于是1+2+4=7
7:00000111
从上面就能看出来,其实就是将各个不同位置的01代码累加起来,而作为基数的1,2,4这样的数列,其实规律也很简单2的n次方,n从0开始。
现在再了解一下什么是位运算
& 按位与
与运算符,是将两个数字的二进制进行,比较,位置相同,且数位都为1,则为1,其他则为0
例如 6 & 3 = 2:
6:00000110
3:00000011
----------
2:00000010
例如 4 & 2 = 0;
4:00000100
2:00000010
----------
0:00000000
| 按位或
或运算符,将两个二进制进行比较,两个相应的二进位中只要有一个为 1,该位的结果就为 1。
例如 6 | 3 = 7
6:00000110
3:00000011
----------
7:00000111
例如 4 | 2 = 6;
4:00000100
2:00000010
----------
6:00000110
^ 按位异或
这个比较有意思,两个二进制进行比较,两个二进制相同则为0,不同则为1.
这里我用一个有意思的案例
int a = 3, b = 2;
我们通常交换 a 和 b 的数值会使用一个temp来作为类似监视哨的变量。
像这样:
temp = a;
a = b;
b = temp;
现在通过 ^ 运算符的话,是可以不需要第三个变量temp来参与的
我们可以:
a = a ^ b;
b = a ^ b;
a = a ^ b;
当然也可以简写:
a ^= b;
b ^= a;
a ^= b;
现在用二进制来分析一下他们三步是怎么运行的。
a = 3, b = 2;
第一步:
a ^ b:
a:00000011
b:00000010
----------
a:00000001
a = 1, b = 2;
第二步:
a:00000001
b:00000010
----------
b:00000011
a = 1, b = 3
第三步:
a:00000001
b:00000011
----------
a:00000010
a = 2, b = 3
--------------
额外的东西:用加减法来交换两个数字
int a = 3, b = 2;
a = a + b; = 3 + 2 = 5;
b = a - b; = 5 - 2 = 3;
a = a - b; = 5 - 3 = 2;
a = 2, b = 3;
~ 取反
这个很好理解,吧二进制都反过来,0变1,1变0
00000110:6
----------
11111001:249
这里的演示是8位的最后一位不是用来区分正负号的
如果使用java的int,或者short,long,byte之类的,他们的最后一位(最左边)01代码是用来区分正负号的。
看这个演示:
System.out.println("~7:" + ~7);
System.out.println("Binary ~7:" + Integer.toBinaryString(~7));
System.out.println("Binary -8:" + Integer.toBinaryString(-8));
输出:
~7:-8
Binary ~7:11111111111111111111111111111000
Binary -8:11111111111111111111111111111000
<< 左移 和 >> 右移
二进制左移或者右移多少位,这个应该是最好理解的。
> 右移" >2 << 3 = 16
00000010:2
00010000:16
终于看完那段枯燥的位运算介绍了,不是吗
其实忘记说了,如果你不想看的话可以跳过就好了。
现在来说说怎么对一个int设置31个开关吧。
还是再啰嗦的说一句吧,一个int有4个字节,一个字节8位,最左边的一位用来表示正负号。
public class Main {
public static void main(String args[]){
int i = 0;
i = setBitComputing(i , 0 ,true);
i = setBitComputing(i , 1 ,false);
i = setBitComputing(i , 2 ,true);
System.out.println(i);
System.out.println(getBitComputing(i, 0));
System.out.println(getBitComputing(i, 1));
System.out.println(getBitComputing(i, 2));
}
public static int setBitComputing(int value,int mask, boolean t) {
if (t) {
value |= (1 << mask);
} else {
value &= ~(1 << mask);
}
return value;
}
public static boolean getBitComputing(int value,int mask) {
return 0 != (value & (1 << mask));
}
}
5
true
false
true
这个案例代码中,我们对int i 进行了第0位
设置为true
,第1位
设置为false
,第2位
设置为true
相当于int 为:00000101 = 5
同样的,我们来获取这个int的第0位
:true
,第1位
:false
,第2位
:true
这样,我们就相当于有了一个31位的开关。
如果你有兴趣,我可以吧这个公式分析一遍,让你更好的了解一下位运算的过程。
这里的代码分为三种,我们来一次解释:
将int i 的某一位设置为true
int i = 0;
setBitComputing(i , 2 ,true);
相当于这一步:
value |= (1 << mask);
将变量替换成具体数字并变形
value = 0 | (1 << 2);
1.计算1 << 2
1:00000001
内存左移两位后:
4:00000100
2.计算 0 | 4
0:00000000
4:00000100
4:00000100
结束,现在i的第二位已经是1了,最右边的是第0位。
将int i 的某一位设置为false
5
true
false
true
获取int i 的某一位是0还是1
getBitComputing(6, 2)
return 0 != (6 & (1 << 2));
1.1 << 2 左移运算 = 4:00000100
2.6 & 4:
6:00000110
4:00000100
----------
4:00000100
3.0 != 4
true
public static filp(value:number, mask:number):number{
let flag = 1 << mask;
return 0 == (value & flag) ? value | flag :value & ~flag;
}
还有很多位运算技巧,可以参考这个帖子:http://blog.csdn.net/zmazon/article/details/8262185
最新评论