使用异或的方式交换两个变量在Java数组中的陷阱
交换两个变量,有很多种方法,就不一一介绍了.
大多数人首先能想到的方法是定义一个临时变量,
如下:
int a = 5;
int b = 10;
int temp = a;
a = b;
b = temp;
此结果就是a = 10,b = 5;
这种方法虽然使用到一个临时变量,性能上可能稍微弱了一点点(达不到显著水平),但是交换数据很安全,可读性也非常高,推荐使用这种方法.
然而一些喜欢体现自己是技术大神的同学,喜欢使用异或进行交换,如下:
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
此时的结果 a = 20; b = 10;
这个方法使用了异或的特性,本身不会有什么问题,由于操作的是二进制进行运算,也没有定义新的变量,理论上性能会高一些,
当然,还可以装大神,显示自己技术大牛,然而它也是有缺陷的,可读性就比较差.
写到这里,异或的方法如果仅仅是可读性差,那么也没必要说它有什么陷阱了.可是真的是这样的吗?
这里的陷阱还需要从选择排序说起.
我们都知道,选择排序的底层实现是这样的:
public static void sort(int[] arr){
for (int i = 1;i <= arr.length - 1 ;i++){
int minindex = i-1;
for (int j = i; j <= arr.length - 1; j ++ ){
if (arr[minindex] > arr[j]){
minindex = j;
}
}
int temp = arr[i -1];
arr[i -1] = arr[minindex];
arr[minindex] = temp;
}
}
习惯使用异或来交换的话就觉得理所当然可以这样写:
public static void sort(int[] arr){
for (int i = 1;i <= arr.length - 1 ;i++){
int minindex = i-1;
for (int j = i; j <= arr.length - 1; j ++ ){
if (arr[minindex] > arr[j]){
minindex = j;
}
}
arr[i -1] = arr[i -1] ^ arr[minindex];
arr[minindex] = arr[i -1] ^ arr[minindex];
arr[i -1] = arr[i -1] ^ arr[minindex];
}
}
假如你是这样认为,那么恭喜你进入了陷阱!
输入数组
int[] arr = {1,4,4,7,8,9,5,4,6};
结果输出竟然变成这样:
[0, 0, 0, 4, 5, 6, 7, 0, 9]
what???
分析结果就知道其原因,并不是因为使用异或的方法不能进行交换,而是因为数组的索引问题带来的!
选择排序原理是将第一个元素假设为最小的一个元素,然后它与之后的元素进行比较,小于这个元素的元素,
就记录最小元素的索引位置,然后将当前最小的元素继续与之后的元素进行比较,直到一轮比较完毕,
再将获得的最小值与假设的最小值位置进行交换.
这样有可能会出现假设的元素就是实际的最小元素,那么在一轮比较下来,出现
minindex = i - 1,而不是minindex = j;
在minindex = i - 1情况下,
假如i = 1简化一下就是:
arr[0] = arr[0] ^ arr[0];
arr[0] = arr[0] ^ arr[0];
arr[0] = arr[0] ^ arr[0];
看到这里,你应该就明白了,索引是0的元素竟然是在异或它本身,而我们知道,变量异或它本身,结果是0啊!
因此使用异或写选择排序,需要判断当前索引有没有改变,即:
if (minindex != i-1){
arr[i -1] = arr[i -1] ^ arr[minindex];
arr[minindex] = arr[i -1] ^ arr[minindex];
arr[i -1] = arr[i -1] ^ arr[minindex];
}
一路分析到这里,我想你也能看出这个陷阱了.
总结就是:
装大神,使用异或交换两个变量是有风险的,技术没学到家会翻车的!
所以我推荐使用第一种方法,简单明了,可读性好,于己于人都有利.