在学习任何语言时,都会提及到一种容易被大家忽视的运算方法–>位运算
在java中,由于JVM机制的存在,使得位运算存在感更加低,并且,为了程序的可读性,也有程序员不愿意使用位运算进行操作。
但是位运算的优秀性能,作为一个程序员是不得不学习,不管是否会用到,都应学习学习其中的原理及思想,这给我们带来的影响是潜移默化的。
基本位运算符 : & | ^ ~ << >> >>
这里咱们就来说说其中的异或 ^
异或,顾名思义就是相异为真
例如:
public class Demo8 {
public static void main(String[] args){
int x=0b11001; // 25
int y=0b10101; // 21
System.out.println(x^y);
// print 12
// 11001^10101=01100
}
}
相同位上存在1和0即为1,其他均为0,也就是异或中出现的1必定是两个二进制数中有一个的对应为上必定为1且另一个数对应位必定为0,即对应位上必定不同,这也就引出了在异或中非常经典的用法,即交换数值
首先,在咱们传统的想法上,交换两数数值如下:
public class Demo8 {
public static void main(String[] args){
int x=1;
int y=2;
int z;
z=x;
x=y;
y=z;
// y=1,x=2
}
}
也就是利用第三个中间变量达到简单直接的交换效果,也是泛用广的一种方法。
聪明一点的同学也可能会发现不需要中间变量也可以实现两数数值交换效果,
如下:
public class Demo8 {
public static void main(String[] args){
int x=1;
int y=2;
x=x+y;
y=x-y;
x=x-y;
// y=1,x=2
}
}
而异或操作,类似与上面的算法,但是原理也正如我上面讲的,提出两数不同的部分,先看例子:
public static void main(String[] args){
int x=11; // 1011
int y=7; // 0111
x=x^y;
// x=1011^0111=1100 1100就是两个二进制数的不同部分
y=x^y;
// y=1100^0111=1011
x=x^y;
// x=1100^1011=0111
System.out.println(x+" "+y);
// print 7 11
}
相信大家之前一定也了解过这种算法,但是其原理或许略知一二
原理很简单,首先,我们将11和7的二进制数1011和0111进行异或,取出他们的不同部分,即1100(下面就称其为不同部分)。拿到不同部分了,然后怎么办呢?
不同部分为1100,就是11为不同的位,00位相同的位,也就是说00位上是两个数都为1或0的位,而11位是两个数只有一个为1的部分,那么,如果我那1100和其中一个数再做异或会怎样呢?为什么会交换数呢?
首先,那到不同部分1100,既然知道11位上必定有一个数为1,有一个数为0,那么,拿去异或,为1的和1异或就为0,就成了另一个数上对应位的值(因为这个数为1,另一个数必定是0),如果1是和0异或,那么相对的就为1了,同样的道理。而至于0值,拿去异或,被异或的数相对位上原本是什么数就该是什么数,不会变(而且这个0位上的值是两个数相同的值)
同样拿上面的11和7举例,x=1011和y=0111,不同部分为1100,拿去和x异或,因为不同点前两位为0,所以x的第一位和第二位保持原样不变,该是0就是0,该是1就是1,而且y上第一位和第二位必定和x的前两位相同,因为只有相同异或出来的结果才为0。而第三位不同部分为1,和x异或后,因为x第三位为0,所以不同,x第三位变为1,但是想想,既然不同部分第三位为1,那么x的第三位和y的第三位必定不同,既然x为0,那么y的第三位必定为1,所以x的第三位顺理成章的就变成的y的第三位,第四位同理,不同部分为1,而x第四位为1,所以异或后必定为0,而既然不同部分为1,那么x第四位和y第四位必定不同,既然x第四位为1,那么y的第四位必定为0,所以异或后x第四位就变成了和y的第四位相同,又因为不同部分的00位必定是x和y相同的,而异或后x和y的不同位又因为异或变成了和y相同的值,也就是说,此时x和不同部分异或后,二进制上变成了y的值了,如果在用y和不同部分异或,同样的道理,就变成了x的值了。
至于为什么没用到第三个数,道理和之前的第二个例子一样,临时用x做了一个存放不同部分的容器,然后y=x^y,因为此时x为不同部分,和y异或后,如上所述,就变成了x的二进制了,如果再让x(不同部分)与此时的y(此时已经变成x的值了)异或,即x=x^y,因为y此时已经为x的值了,所以不同部分和x异或后,自然会得到y的二进制,即y的值,也就是说此时x为y的值。
综上所述,就完成了我上面那个例子的整个过程。
如果有些朋友还不能理解,可以拿笔在纸上简单画一画,很快就能理解了。
位运算的优势无非是在于直接在机器层面对数进行操作了,因为计算机的数据存储均为二进制,所以理论上是能操作所有数值的,当然,具体还得根据情况而定,不然也会得到出人意料的结果。
至于位运算的取舍,有人不愿意为了提升这百分之几的性能而混淆代码的可读性,有人又认为性能才是程序之根本,尤其是在进行嵌入式等底层开发上,所以这就是仁者见仁智者见智了。