巴拉巴拉
小魔仙

位运算,一个int能设置好多个开关

通常我们在程序里面会保存很多的开关,那么每个开关就要占一个字段

一个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

赞(2) 打赏
如果文章对你有帮助,欢迎你来评价反馈。AgainFly » 位运算,一个int能设置好多个开关

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  • Q Q(选填)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏