今天在学习算法时涉及到了不停的交换两个变量的值的操作,就在想有没有什么高大上的方式来实现交换呢?
回想起以前茶语饭给给朋友出过这道问题,他们也真的给了我很多思路
当时很是感慨,每个人的思想真的就是不一样,人家的思想偏偏就是你没有的,也是你值得学习的地方!
今天要介绍的内容也都写在标题中了,有可能下面要讲的远不止你想像的.
各大平台上也都有讲到这个知识,但我仍然想记录下自己的思路,重在思想!
下面会通过基本数据类型变量值交换以及引用数据类型不改变地址情况实现值交换进行介绍!
一.基本数据类型值交换(这里以int为例)
1.位运算交换(不适用与交换两个相等的值,需要自行加入判断)
- ^是异或运算符,异或的规则是转换成二进制比较,相同为0,不同为1
- 值A两次亦或值B,其值仍为A,下面也是利用的这一点
/**
* 位运算交换两个变量值(不存在第三方)
* 只针对整型
* @param a
* @param b
*/
public static void changePosByBit(int a, int b){
System.out.println("交换前 ::: ["+ a +","+ b +"]");
a = a^b;
b = a^b;
a = a^b;
System.out.println("交换后 ::: ["+ a +","+ b +"]");
}
测试数据
int a = 10;
int b = 11;
控制台输出:
交换前 ::: [10,11]
交换后 ::: [11,10]
2.算数运算(这个是我朋友想到的,也是我忽略的)
/**
* 算数运算交换两个变量值(不存在第三方)
* @param a
* @param b
*/
public static void changePosByOperation(int a, int b){
System.out.println("交换前 ::: ["+ a +","+ b +"]");
a = b - a;
b = b - a;
a = a + b;
System.out.println("交换后 ::: ["+ a +","+ b +"]");
}
测试数据
int a = 10;
int b = 11;
控制台输出:
交换前 ::: [10,11]
交换后 ::: [11,10]
二.栈完成所有数据类型的指向地址交换
/**
* 栈实现交换两个变量值(不存在第三方)
* 针对所有数据类型(只交换指向的内存地址)
* @param objA
* @param objB
* @param clazz
* @param <T>
*/
public static <T> void changePosByStack(Object objA,Object objB,Class<T> clazz){
System.out.println("交换前->[objA::"+(T)objA+"::"+getAddress(objA)+"]");
System.out.println("交换前->[objB::"+(T)objB+"::"+getAddress(objB)+"]");
Stack stack = new Stack();
stack.push(objA);
stack.push(objB);
objA = stack.pop();
objB = stack.pop();
System.out.println("交换后->[objA::"+(T)objA+"::"+getAddress(objA)+"]");
System.out.println("交换后->[objB::"+(T)objB+"::"+getAddress(objB)+"]");
}
测试数据:
String strA = "hello";
String strB = "world";
控制台输出:
交换前->[objA::hello::21685669]
交换前->[objB::world::2133927002]
交换后->[objA::world::2133927002]
交换后->[objB::hello::21685669]
三.引用数据类型不改变地址只交换值(这里以String为例)
像以往我们交换此类型变量通常是交换了两个变量的指向地址
值得思考的是如何不改变各自地址就可以实现值交换呢?答案是利用反射技术!
问题又来了,大家经常讨论而且面试也经常问"字符串的值可变吗?"答案其实是可变!
String为什么给大家一个不可变的印象呢?原因↓↓↓
所以说,如果能利用反射获取修改成员属性value[]的权限的话,就可以达成我们的目的了.
而且,事实证明是可以做到的,哪怕这个成员属性被final修饰者,也能办到!
/**
* 交换两个字符串变量的值(在内存地址不变的前提下)
* @param strA
* @param strB
*/
public static void changeValueStr(String strA,String strB) throws Exception {
System.out.println("交换前->[strA::"+strA+"::"+getAddress(strA)+"]");
System.out.println("交换前->[strB::"+strB+"::"+getAddress(strB)+"]");
String temp = new String(strA);
Field fieldA = strA.getClass().getDeclaredField("value");
fieldA.setAccessible(true);
fieldA.set(strA,strB.toCharArray());
Field fieldB = strB.getClass().getDeclaredField("value");
fieldB.setAccessible(true);
fieldB.set(strB,temp.toCharArray());
System.out.println("交换后->[strA::"+strA+"::"+getAddress(strA)+"]");
System.out.println("交换后->[strB::"+strB+"::"+getAddress(strB)+"]");
}
测试数据:
String strA = "hello";
String strB = "world";
控制台输出:
交换前->[strA::hello::356573597]
交换前->[strB::world::1735600054]
交换后->[strA::world::356573597]
交换后->[strB::hello::1735600054]
不难发现,各自地址并未被修改,但是值被交换了!
四.总结
基本数据类型不使用第三方变量进行值交换就类似于智力游戏(数学奥赛题)
如何能写出所有数据类型通用交换值的方式便更有意思
同时引用数据类型不修改地址值前提修改值貌似又让你离Java更亲近了一步
上面只列举了几个典型,若有其它,举一反三!