java位运算

java位运算

位运算表达式由操作数和位运算符组成,实现对整数类型的二进制数进行位运算。位运算符可以分为逻辑运算符(包括~|^)及移位运算符(包括>><<>>>)。

二进制表示

二进制第一位为符号位,0表示正数,1表示负数。

1.正数表示,例如:int类型值 5(int用32位表示)

5 = 0000 0000 0000 0000 0000 0000 0000 0101

2.负数表示,例如:int类型值 -5

-5 = 1111 1111 1111 1111 1111 1111 1111 1011

负数二进制表示方法如下:

首先来个正数5

5 = 0000 0000 0000 0000 0000 0000 0000 0101

这个就是原码,然后原码取反(0的变成1,1的变成0),得到反码

原码: 0000 0000 0000 0000 0000 0000 0000 0101
反码: 1111 1111 1111 1111 1111 1111 1111 1010

反码进行加1得到补码

原码: 0000 0000 0000 0000 0000 0000 0000 0101
反码: 1111 1111 1111 1111 1111 1111 1111 1010
补码: 1111 1111 1111 1111 1111 1111 1111 1011

最终得到的补码就是负数在计算机中的二进制表示方法

3.二进制转换为十进制

正数不讲,略…, 这里只说负数,先随便来一个负数二进制

负数二进制: 1111 1111 1111 1111 1111 1111 1111 1011

负数二进制,减1

负数二进制: 1111 1111 1111 1111 1111 1111 1111 1011
二进制减一: 1111 1111 1111 1111 1111 1111 1111 1010

取反

负数二进制: 1111 1111 1111 1111 1111 1111 1111 1011
二进制减一: 1111 1111 1111 1111 1111 1111 1111 1010
减一后取反: 0000 0000 0000 0000 0000 0000 0000 0101

然后计算得到结果为5,那么负数二进制就为-5

逻辑运算符

1.非(~)运算 : 取反

System.out.println(~5);// 结果为-6 

5  = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
~5 = 1111 1111 1111 1111 1111 1111 1111 1010 = -6

2.与 (&)运算 : 有0出0;全1出1

System.out.println(5 & 3);// 结果为1

5     = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
3     = 0000 0000 0000 0000 0000 0000 0000 0011 = 3
5 & 3 = 0000 0000 0000 0000 0000 0000 0000 0001 = 1

3.或(|)运算 : 有1出1;全0出0

System.out.println(5 | 3);// 结果为7

5     = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
3     = 0000 0000 0000 0000 0000 0000 0000 0011 = 3
5 | 3 = 0000 0000 0000 0000 0000 0000 0000 0111 = 7

4.异或(^)运算: 相同得0;相异得1

System.out.println(5 ^ 3);//结果为6 

5     = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
3     = 0000 0000 0000 0000 0000 0000 0000 0011 = 3
5 ^ 3 = 0000 0000 0000 0000 0000 0000 0000 0110 = 6

小应用

A. 一个数字标识位表示多种逻辑,例如:一个任务可分成n个独立小任务,每个小任务之间不分先后顺序,当所有小任务完成则表示此任务完成。比较用 & ,加用 |

如果n=3,则将3个小任务分别用$2^0$ , $2^1$ , $2^2$即1,2,4表示

//更新当前任务完成第1个小任务
update tbl_task set status = status|1 where id=1;
//同理,更新当前任务完成第2个小任务
update tbl_task set status = status|2 where id=1;
//同理,更新当前任务完成第3个小任务
update tbl_task set status = status|4 where id=1;
//当status = 7时,则表示三个任务都完成;

//查询第1个小任务未完成的所有任务
select * from tbl_task where status&1=0;
//查询第1个小任务已完成的所有任务
select * from tbl_task where status&1=1;

//同理,第2个小任务
//查询第2个小任务未完成的所有任务
select * from tbl_task where status&2=0;
//查询第2个小任务已完成的所有任务
select * from tbl_task where status&2=2;

B. 利用异或运算的自反性交换两个数

public void exchange(int a, int b) {
	a ^= b;
	b ^= a;
	a ^= b;
}

C.一个数组存放若干整数,一个数出现奇数次,其余数均出现偶数次,找出这个出现奇数次的数?

最直接的办法就是把所有数异或 (奇数个异或是本身,偶数个是0)

D.1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?

解法一:将所有数加起来,减去1+2+…+1000的和。

这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。 解法二:异或就没有这个问题,并且性能更好。将所有的数全部异或,得到的结果与1^2^3^…^1000的结果进行异或,得到的结果就是重复数。

解法一很显然,解法二需要证明一下:

前面提到异或具有交换律和结合律,所以1^2^…^n^…^n^…^1000^,无论这两个n出现在什么位置,都可以转换成为1^2^…^1000^(n^n)的形式。 其次,对于任何数x,都有x^x=0,x^0=x。 所以1^2^…^n^…^n^…^1000 = 1^2^…^1000^(n^n)= 1^2^…^1000^0 = 1^2^…^1000(即序列中除了n的所有数的异或)。 令,1^2^…^n^..^1000(序列中包含一个n)的结果为T 则1^2^..^n^..^n^..^1000^(序列中包含2个n)的结果就是T^n。 T^(T^n)=n。 所以,将所有的数全部异或,得到的结果与1^2^3^…^1000的结果进行异或,得到的结果就是重复数。

移位运算符

1.左移位运算符(<<)能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补0)。

System.out.println(5 << 3);// 运行结果是40

5      = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
5 << 3 = 0000 0000 0000 0000 0000 0000 0010 1000 = 40

2.“有符号”右移位运算符(>>)则将运算符左边的运算对象向右移动运算符右侧指定的位数。 “有符号”右移位运算符使用了“符号扩展”:若值为正,则在高位插入0;若值为负,则在高位插入1。

System.out.println(5 >> 3);// 运行结果是0

5      = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
5 >> 3 = 0000 0000 0000 0000 0000 0000 0000 0000 = 0

System.out.println(-5 >> 3);// 结果是-1

-5      = 1111 1111 1111 1111 1111 1111 1111 1011 = -5
-5 >> 3 = 1111 1111 1111 1111 1111 1111 1111 1111 = -1

3.Java也添加了一种“无符号”右移位运算符(>>>),它使用了“零扩展”:无论正负,都在高位插入0。这一运算符是C或C++没有的。

System.out.println(5 >>> 3);// 结果是0

5       = 0000 0000 0000 0000 0000 0000 0000 0101 = 5
5 >>> 3 = 0000 0000 0000 0000 0000 0000 0000 0000 = 0

System.out.println(-5 >>> 3);// 结果是536870911

-5       = 1111 1111 1111 1111 1111 1111 1111 1011 = -5
-5 >>> 3 = 0001 1111 1111 1111 1111 1111 1111 1111 = 536870911

打赏一个呗

取消

感谢您的支持!!!

扫码支持
扫码支持
扫码打赏

打开支付宝或微信扫一扫,即可进行扫码打赏哦