今天在學習算法時涉及到了不停的交換兩個變量的值的操作,就在想有沒有什麼高大上的方式來實現交換呢?
回想起以前茶語飯給給朋友出過這道問題,他們也真的給了我很多思路
當時很是感慨,每個人的思想真的就是不一樣,人家的思想偏偏就是你沒有的,也是你值得學習的地方!
今天要介紹的內容也都寫在標題中了,有可能下面要講的遠不止你想像的.
各大平臺上也都有講到這個知識,但我仍然想記錄下自己的思路,重在思想!
下面會通過基本數據類型變量值交換以及引用數據類型不改變地址情況實現值交換進行介紹!
一.基本數據類型值交換(這裏以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更親近了一步
上面只列舉了幾個典型,若有其它,舉一反三!