位运算案例

总结: Java位运算是针对于整型(byte、char、short、int、long)数据类型的二进制进行的移位操作。

 

位运算认知*

数据类型

位数

byte

8

boolean

8

short

16

int

32

long

64

float

32

double

64

char

16

 

位运算符认知*

 

优先级S:~ 波浪      

   优先级A:<<、>>和>>>          

      优先级B:&              

         优先级C:^

             优先级D:|

 

 

位运算符初使用*

 

场景1:判断奇偶数 a&1 结果为 0 ,a就是偶数 结果为 1 ,a就是奇数 (奇数第一位肯定是1)

int c = 8;

// 8 = 0000 1000

// 1 = 0000 0001

// & = 0000 0000

System.out.println(c & 1); //0

c = 129;

// 129 = 1000 0001

// 1 = 0000 0001

// & = 0000 0001

System.out.println(c & 1); //1

 

场景2:求平均数 (x+y)/2 这样吗?考虑过 x+y可能超过int的范围吗?正确的姿势是 (x&y)+((x^y)>>1)

// x+y = 奇数,例137,该算法 = 68; x+y = 偶数,例136,该算法 = 68;

 

场景3:有两个int类型变量x、y,要求两者数字交换,不用临时变量?x ^= y; y ^= x; x ^= y;

int x = 8;  //0000 1000
int y = 128; //1000 0000

//x^=y这个表达式相当于x=x^y
// x = 0000 1000
// y = 1000 0000
// ^ = 1000 1000    x = 136
        x ^= y;

//y^=x这个表达式相当于y=y^x
// y = 1000 0000
// x = 1000 1000
// ^ = 0000 1000    y = 8
        y ^= x;

//x^=y这个表达式相当于x=x^y
// x = 1000 1000
// y = 0000 1000
// ^ = 1000 0000    x = 128
x ^= y;
System.out.println(x); //128
System.out.println(y); //8

 

场景4:求绝对值 ( 绝对值是一种运算,运算符号通常用||表示。这种运算的意义是:一个正数和0的绝对值是它本身,一个负数的绝对值是它的相反数。总之,一个数的绝对值是非负数.)

int abs( int x ) { 

int y= x >> 31 ; 

return (x^y)-y ; //or: (x+y)^y

 }

//计算机表示负数:需要使用“二进制的补数”
// 为了获得补数,需要将二进制的各数位数值全部取反,再讲结果+1
//8   =      0000 0000 0000 0000 0000 0000 1000
//取反 =      1111 1111 1111 1111 1111 1111 0111
// +1 =      1111 1111 1111 1111 1111 1111 1000
int xx = -8;  //1111 1000
//按位右移31位: 1111 1111 1111 1111 1111 1111 1111
int yy = xx >> 31;
System.out.println(yy); //-1
//xx         = 1111 1111 1111 1111 1111 1111 1000
//yy         = 1111 1111 1111 1111 1111 1111 1111
//(xx^yy)    = 0000 0000 0000 0000 0000 0000 0111 = 7

//(xx^yy)-yy = 7-(-1) = 8
System.out.println((xx^yy)-yy); //-8

 

场景8:求相反数 (~x+1)

int x = 8; //0000 1000

//~ = 1111 0111 = -9

// -9 + 1 = -1

System.out.println(~x+1);  //-8

 

场景9:指定位数比较

//处理整数类型,可以直接对组成整数类型的各个位进行操作。这意味着可使用掩码技术(转二进制)得到整数中的各个位:
//十进制8 转换二进制 = 00001000
int n = 8;
//0b开头表二进制; 0开头表八进制; 0x开头表十六进制;
//ob10000000二进制字面量默认为int 四个字节 = 128; (byte)ob10000000 = -128
//变量n 二进制后的第四位 = 1, 则返回1,否返回0
int a = (n & 0b1000) / 0b1000;
System.out.println(a); //输出1

位运算符:逻辑推理题 - 小白鼠试毒*

  有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,问至少要多少只小白鼠才能在24小时鉴别出哪瓶水有毒,刚看类似的问题,一般人一定是一脸懵逼的状态的,分析一下,喝了带毒的水24小时才会毒发,目前需求是要24小时就要鉴别出来哪瓶有毒,所以小白鼠只够喝一次水的时间,完全摸不到头脑……

解决办法

  给1000个瓶分别标上如下标签(10位长度): 0000000001 (第1瓶) 0000000010 (第2瓶) 0000000011 (第3瓶) ...... 1111101000 (第1000瓶) 从编号最后1位是1的所有的瓶子里面取出1滴混在一起(比如从第一瓶,第三瓶,。。。里分别取出一滴混在一起)并标上记号为1。以此类推,从编号第一位是1的所有的瓶子里面取出1滴混在一起并标上记号为10。现在得到有10个编号的混合液,小白鼠排排站,分别标上10,9,。。。1号,并分别给它们灌上对应号码的混合液。24小时过去了,过来验尸吧: 从左到右,死了的小白鼠贴上标签1,没死的贴上0,最后得到一个序号,把这个序号换成10进制的数字,就是有毒的那瓶水的编号。 检验一下:假如第一瓶有毒,按照0000000001 (第1瓶),说明第1号混合液有毒,因此小白鼠的生死符为0000000001(编号为1的小白鼠挂了),0000000001二进制标签转换成十进制=1号瓶有毒;假如第三瓶有毒,0000000011 (第3瓶),第1号和第2号混合液有毒,因此小白鼠的生死符为00000011(编号为1,2的鼠兄弟挂了),0000000011二进制标签转换成十进制=3号瓶有毒。

解决思路

  逻辑上来解释这个问题,首先看瓶子,二进制数,10位能够表示的最大数位2的10次方-1=1023,大于1000,所以足够覆盖1000以内的二进制展示,有毒的那瓶水的二进制插入在竖直排列的数据中,混合液为获取当前二进制位上为1的所有水的混合,当小白鼠喝了over以后,说明有毒的水当前二进制位上的数值是1,否则,说明当前二进制的数值为0,所以当小白鼠最终是否over,根据转换后的十进制就能获取到有毒的那瓶水。

 

项目实战*

例如,在一个系统中,用户一般有查询(Select)、新增(Insert)、修改(Update)、删除(Delete)四种权限,四种权限有多种组合方式,也就是有16中不同的权限状态(2的4次方)。

public class Permission {
	
	// 是否允许查询
	private boolean allowSelect;
	
	// 是否允许新增
	private boolean allowInsert;
	
	// 是否允许删除
	private boolean allowDelete;
	
	// 是否允许更新
	private boolean allowUpdate;

	// 省略Getter和Setter
}

//下面是另外一种方式,使用位掩码的话,用一个二进制数即可,每一位来表示一种权限0表示无权限,1表示有权限。

 

ALLOW_SELECT = 1 << 0 转成二进制就是0001,二进制第一位表示Select权限。 ALLOW_INSERT = 1 << 1 转成二进制就是0010,二进制第二位表示Insert权限。

private int flag存储了各种权限的启用和停用状态,相当于代替了Permission中的四个boolean类型的变量。

public class NewPermission {
	// 是否允许查询,二进制第1位,0表示否,1表示是
	public static final int ALLOW_SELECT = 1 << 0; // 0001
	
	// 是否允许新增,二进制第2位,0表示否,1表示是
	public static final int ALLOW_INSERT = 1 << 1; // 0010
	
	// 是否允许修改,二进制第3位,0表示否,1表示是
	public static final int ALLOW_UPDATE = 1 << 2; // 0100
	
	// 是否允许删除,二进制第4位,0表示否,1表示是
	public static final int ALLOW_DELETE = 1 << 3; // 1000
	
	// 存储目前的权限状态
	private int flag;

	/**
	 *  重新设置权限
	 */
	public void setPermission(int permission) {
		flag = permission;
	}

	/**
	 *  添加一项或多项权限
	 */
	public void enable(int permission) {
		flag |= permission; // 相当于flag = flag | permission;
	}
	
	/**
	 *  删除一项或多项权限
	 */
	public void disable(int permission) {
		flag &= ~permission;
	}
	
	/**
	 *  是否拥某些权限
	 */
	public boolean isAllow(int permission) {
		return (flag & permission) == permission;
	}
	
	/**
	 *  是否禁用了某些权限
	 */
	public boolean isNotAllow(int permission) {
		return (flag & permission) == 0;
	}
	
	/**
	 *  是否仅仅拥有某些权限
	 */
	public boolean isOnlyAllow(int permission) {
		return flag == permission;
	}
}

flag的四个二进制位来表示四种权限的状态,每一位的0和1代表一项权限的启用和停用,下面列举了部分状态表示的权限:

flag

删除

修改

新增

查询

 

1(0001)

0

0

0

1

只允许查询(即等于ALLOW_SELECT)

2(0010)

0

0

1

0

只允许新增(即等于ALLOW_INSERT)

4(0100)

0

1

0

0

只允许修改(即等于ALLOW_UPDATE)

8(1000)

1

0

0

0

只允许删除(即等于ALLOW_DELETE)

3(0011)

0

0

1

1

只允许查询和新增

0

0

0

0

0

四项权限都不允许

15(1111)

1

1

1

1

四项权限都允许

使用位掩码的方式,只需要用一个大于或等于0且小于16的整数即可表示所有的16种权限的状态。

//调用这个方法可以在现有的权限基础上添加一项或多项权限。

public void enable(int permission) { flag |= permission; // 相当于flag = flag | permission; }

//添加一项Update权限:

permission.enable(NewPermission.ALLOW_UPDATE);

假设现有权限只有Select,也就是flag是0001。执行以上代码,添加Update权限,flag = 0001 | 0100,也就是0101,便拥有了Select和Update两项权限。

解释: 查看权限 0001

           修改权限 0100

           flag = 0101 //对应位都是0,则为0,否则为1

 

//添加Insert、Update、Delete三项权限:

permission.enable(NewPermission.ALLOW_INSERT | NewPermission.ALLOW_UPDATE | NewPermission.ALLOW_DELETE);

//运算结果 flag = 1110

 

两者对比

设置仅允许Select和Insert权限

//Permission类

permission.setAllowSelect(true); permission.setAllowInsert(true); permission.setAllowUpdate(false); permission.setAllowDelete(false);

//NewPermission类

permission.setPermission(NewPermission.ALLOW_SELECT | NewPermission.ALLOW_INSERT);

 

判断是否同时有Select和Insert、Update权限

//Permission类

if (permission.isAllowSelect() && permission.isAllowInsert() && permission.isAllowUpdate())

//NewPermission类

if (permission.isAllow (NewPermission.ALLOW_SELECT | NewPermission.ALLOW_INSERT | NewPermission.ALLOW_UPDATE))

/**

* 是否拥某些权限

*/

public boolean isAllow(int permission) {   //permission = 0111

          // 假设flag = 0111

          // (flag & permission) = 0111 & 0111 = 0111 //&相对位都为1,则为1,否则为0

          // 0111 = 0111 返回true

          return (flag & permission) == permission;

}

 

源码*

java.lang.reflect.Modifier

在Java反射中,java.lang.reflect.Modifier是用来判断类、成员变量、方法等包含的修饰符。在Modifier的源代码中可以看到:

public static final int PUBLIC           = 0x00000001;
public static final int PRIVATE          = 0x00000002;  
public static final int PROTECTED        = 0x00000004;  
public static final int STATIC           = 0x00000008;  
public static final int FINAL            = 0x00000010;  
public static final int SYNCHRONIZED     = 0x00000020;
// ......
public static boolean isProtected(int mod) {
	return (mod & PROTECTED) != 0;
}
public static boolean isStatic(int mod) {
	return (mod & STATIC) != 0;
}

以上资料来源:

https://www.cnblogs.com/yuanhailiang/p/9479105.html

http://xxgblog.com/2013/09/15/java-bitmask/

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值