我们都知道逻辑运算上有与(&&)、或(||)、非(!)三个运算。在计算机数值计算中,按位运算一共有6种:左移(<<)、右移(>>)、与(&)、或(|)、取反(~)、异或(^)运算。
1、定义:
1)按位与运算(&):双目运算符,其功能是参与运算的两数对应的二进位相与,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。
例如:
9&5=1
00001001 (9的补码)
&
00000101 (5的补码)
=
00000001 (1的二进制补码)。
应用:
- 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
- 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)
2)按位或运算(|):双目运算符,其功能是参与运算的两数对应的二进位相或,只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。
例如:
9|5=13,可写算式如下:
00001001 (9的补码)
|
00000101 (5的补码)
=
00001101 (十进制为13)
应用:
- 常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)
3)按位异或运算(^):双目运算符,其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现。
例如:
9^5=12,可写成算式如下:
00001001 (9的补码)
^
00000101 (9的补码)
=
00001100 (十进制为12)
应用:
- 与0相∧,保留原值;
- 两个相同数异或,结果为0;
- 异或操作满足结合律和交换律;
- 将源操作数某些位翻转(1变0,0变1), (mask中特定位置1,其它位为0 s=s^mask)
- 交换两个值,不用临时变量;(如下)
a = a^b;
b = a^b;
a = a^b;
4)取反运算(~):单目运算符,其功能是对参与运算的数的各二进位按位求反。
例如:
~9的运算为:
~(0000000000001001)结果为:1111111111110110
参考:https://blog.csdn.net/hguisu/article/details/7892596
2、使用二进制位运算设计权限:
1)原理:
我们都知道在linux中,x - 可执行权限,w - 可写权限 , r - 可读权限。其权限值分别是1,2,4。有没有想过为什么是1,2,4 而不是 1,2,3 呢?
观察发现,这些权限对应的int数值都是2的幂次方,对应的二进制如如下:
1:0000 0000 0000 0000 0000 0000 0000 0001
2:0000 0000 0000 0000 0000 0000 0000 0010
4:0000 0000 0000 0000 0000 0000 0000 0100
8:0000 0000 0000 0000 0000 0000 0000 1000
这些值在二进制表示中只有一位对应的是1,其余都是0,这样多个权限值进行叠加后不会相互覆盖,而且根据叠加后的权限值能否分解成对应的原来权限值。举一个反例:如果权限的原值用1,2,3表示,那么当给定一个权限3时,这个值就不知道是1和2叠加的权限,还是3原本的权限。
使用二进制的方式来表示权限的优点:
- 运算速度块(位运算在计算机内部非常高效)
- 通过按位与、或、异或操作高效的进行权限添加、删除和判断。
2)看一个例子:
private static void quanxianTest() {
//1.定义权限:权限值使用int类型表示,且满足2的N次方
int c = 1;//create,二进制表示:...0 0001
int r = 2;//retrieve,二进制表示 ...0 0010
int u = 4;//update,二进制表示 ...0 0100
int d = 8;//delete,二进制表示 ...0 1000
//其他权限:16、32、64...;
//2.初始化权限
int usera = c | r | u;//初始化用户A的权限,等价于c+r+u=7
int userb = c | d;//初始化用户B的权限,等价于c+d=9
int userc = 13;//初始化用户C的权限,即:c+u+d=13
System.out.println("usera 权限值:"+usera);
System.out.println("userb 权限值:"+userb);
System.out.println("userc 权限值:"+userc);
//3.判断权限
if ((usera & u) == u) {//与操作结果只有两种:0或u
System.out.println("usera有u权限");
}
if ((userb & u) == 0) {
System.out.println("userb没有u权限");
}
if ((userc & c) != 0) {
System.out.println("userc有c权限");
}
//添加权限
userb = userb | u;//或者userb = userb + u;
if ((userb & u) == u) {
System.out.println("userb有u权限,userb的权限值="+userb);
}
//删除权限
usera = usera & (~r);//或者usera = usera - r;
if ((usera & r) == 0) {
System.out.println("user a没有r权限,usera的权限值="+usera);
}
}
3)总结:
使用二进制的与或非来设计权限的步骤:
- 定义权限,权限的int值分别是2的幂次方,即1、2、4、8...
- 添加权限时使用或(|)运算,最终权限值在数值上=对应的权限值加在一起;
- 删除权限时使用与(&)和异或(~)运算,最终权限值在数值上=原权限值-删除的权限值
- 判断权限是使用与(&)运算,结果等于非零(或对应权限的值)时表示有该权限,结果等于0表示没有该权限;
注:只有当权值是2的幂次方时,或(|)运算相当于加法、与(&)运算相当于减法。
https://www.cnblogs.com/jifeng/archive/2013/03/03/2941159.html
3、使用二进制位运算存储数据信息:(类似bitmap)
在设计程序时,我们经常会运到一种情况:某个类有多个属性值时,且这些属性的取值为1和0。这时,我们的常规思路是每个属性使用一种类型表示(boolean或int),优点是清晰,缺点是浪费空间(假设我们有2kw个这样的对象)。
为了节省空间,我们使用一个int类型的变量,来表示类的所有属性。int类型变量值转成二进制位,每一位表示一个属性值(0或1)——bitMap算法思想。
1)原理:
根据位运算的定义,我们可以得出以下结论:
- 将int型变量a的第k位清0:即a=a&~(1<<k)
- 将int型变量a的第k位置1:即a=a|(1<<k)
- 取int型变量a的第k位:即a>>k&1
例如:
将31的第8位设置成1,可以表示成:31的二进制|(1<<8)
1)31的二进制:
00000000 00000000 00000000 00011111
2)1的二进制:
00000000 00000000 00000000 00000001
左移动8位:
00000000 00000000 00000001 00000000
3)31|(1<<8)表示成:
00000000 00000000 00000000 00011111
|
00000000 00000000 00000001 00000000
=
00000000 00000000 00000001 00011111
注:int类型表示成的二进制数据中,最右边是第0位,左移k位,就是将第0位移动到第k位,右边补0.
2)示例:
private static void test0() {
int a = 31;
System.out.println(toBinaryString(a));//按2进制输出
//00000000 00000000 00000000 00011111
//将int型变量a的第k位清0,即a=a&~(1<<k)
int b = a & ~(1<<3);
System.out.println(toBinaryString(b));
//00000000 00000000 00000000 00010111
//将int型变量a的第k位置1, 即a=a|(1<<k)
int c = a | (1<<8);
System.out.println(toBinaryString(c));
//00000000 00000000 00000001 00011111
//取int型变量a的第k位,(k=0,1,2……sizeof(int)),即a>>k&1
int d = a >> 2 & 1;
System.out.println(d);//1
int d2 = a & (1<<2);
System.out.println(d2);//4
}
public static String toBinaryString(int a) {
StringBuilder sb = new StringBuilder();
for(int i=0;i<32;i++){
int t = (a & 0x80000000>>>i)>>>(31-i);
//System.out.println(t);
sb.append(t);
if ((i+1) % 8 ==0) {
sb.append(" ");
}
}
return sb.toString();
}