JAVA 學習(一):16進位制字串自增的實現

kingzhsh發表於2018-12-01

JAVA學習系列,並不是從基礎去講java的知識,而是把我在學習或是工作中,一些思想、邏輯總結出來。

原先在工作中,因為測試的需要,經常要往資料庫中批量的插資料。而表的主鍵用的是UUID,是由16進位制字元加“-”組成的,還有裝置的mac地址是由16進位制字元加“:”組成的,那個時候,我剛學java,也不知道java裡面有方法,可以生成UUID,所以我當時的解決方案是,給定一個初始的UUID,然後讓UUID自增。當然,實現自增是需要做些處理的。UUID是由32個16進位制字元組成,如果轉換成二進位制的話,就是128位,而在java裡面,一個long型別的數字,也才64位,所以一個UUID要轉換成數字的話,需要兩個long來表示。所以,最終的處理邏輯就是:UUID先去掉分隔符“-”,再分成兩半,兩部分再轉換成long,自增以後,再轉換成16進位制字串,再拼接“-”,變成需要的字串。當然mac的自增與這個類似。後來,我就思考,這字串轉成數字,自增以後又轉換成字串,轉來轉去的,可不可以不轉換成數字就實現自增?基於這樣的思想,就開始了我的探索之路。

要實現16進位制字串的自增,先從簡單的做起,從一位數的加法做起,比如'f'+5,要怎麼實現?那麼要實現這個加法,需要解決三個問題:1. 把'f'轉換成數字,再進行運算。2.運算之後的結果,再轉換回16進位制字串。3.進位問題。解決方法就是:在方法內維護一個字元陣列,元素是0-9a-f這幾個字元,然後有了'f',那麼就查詢'f'在陣列中的下標,下標是幾,那麼這個字元就轉換成數字幾。查詢後,'f'在陣列中的下標是15,那是'f'就轉換成數字15,用15+5得到運算結果20,然後再把20轉換成16進位制,那麼把運算結果轉換成16進位制,存在兩種情況,一種是運算結果小於16,這個時候不需要進位,結果是幾就是幾,比如結果是11,不需要進位,那麼就到字元陣列中查詢下標是11的元素,結果是'b',那麼最終的結果就會顯示為“b”。那麼另外一種情況就是運算結果大於等於16了,這個時候需要進位,那麼進位時,要進幾,進位後本位是多少?進位數為運算結果除以16,進位後本位就是運算結果模以16。邏輯理清了,那麼就是程式碼的實現了

package aaa;

public class Demo {
	public static void main(String[] args){
		hexAdd('f', 5);
	}
	
	public static void hexAdd(char hex, int num){
		char[] hexs = "0123456789abcdef".toCharArray();
		int index = indexOfCharArray(hexs, hex);
		if(index == -1){
			System.out.println("所傳的字元引數非法!");
		}else{
			int sum = index+num;
			if(sum<16){
				System.out.println(hexs[sum]);
			}else{
				System.out.println(""+hexs[sum/16]+hexs[sum%16]);
			}
		}
	}
	/**
	 * 獲取字元在字元陣列中的下標
	 * 引數一:字元陣列
	 * 引數二:字元
	 * 返回值:字元在字元陣列中的下標,如果字元陣列中沒有該字元,返回-1
	 */
	public static int indexOfCharArray(char[] charArray, char c){
		for(int i=0, len=charArray.length; i<len; ++i){
			if(charArray[i]==c){
				return i;
			}
		}
		return -1;
	}
}

執行之後,結果就會列印14,這就是'f'+5的結果。再接下來,就是實現多位16進位制字元的自增了

package aaa;

public class Demo {
	public static char[] hexs = "0123456789abcdef".toCharArray();
	
	public static void main(String[] args){
		String result = hexAdd("abc", 256);
		System.out.println(result);
	}
	
	public static String hexAdd(String hex, int num){
		int len = hex.length()-1;
		//是否需要進位
		boolean b = true;
		//參與了運算的位,運算之後的結果字串,
		//由右向左運算,首先是最後一位參與運算,如果需要進位,那麼倒數第二位也要參與運算。以些類推
		String change = "";
		//最終運算結果,由未參與運算的位和參與了運算的位兩部分組成
		String result = "";
		while(b){
			char c = hex.charAt(len);
			int index = indexOfCharArray(hexs, c);
			if(index == -1){
				return "所傳的字串引數非法!";
			}else{
				int sum = index+num;
				if(sum<16){
					change = hexs[sum]+change;
					result = hex.substring(0, len)+change;
					b = false;
				}else{
					change = hexs[sum%16]+change;
					num = sum/16;
				}
			}
			--len;
		}
		
		return result;
	}
	/**
	 * 獲取字元在字元陣列中的下標
	 * 引數一:字元陣列
	 * 引數二:字元
	 * 返回值:字元在字元陣列中的下標,如果字元陣列中沒有該字元,返回-1
	 */
	public static int indexOfCharArray(char[] charArray, char c){
		for(int i=0, len=charArray.length; i<len; ++i){
			if(charArray[i]==c){
				return i;
			}
		}
		return -1;
	}
}

這樣就簡單實現了16進位制字串的自增了,但現在還有問題,如果運算出來的結果,比原先的16進位制字串位數多的話,那麼就會出錯,下標越界。比如"ab",然後自增一個非常大的數,依次進位,最終結果大於2位的話,就會報錯,在while迴圈裡面,最後一行--len,會出現負數,然後下一次迴圈,用這個負數作下標去原字串中取字元,就下標越界了。改:

package aaa;

public class Demo {
	public static char[] hexs = "0123456789abcdef".toCharArray();
	
	public static void main(String[] args){
		String result = hexAdd("c", 256);
		System.out.println(result);
	}
	
	public static String hexAdd(String hex, int num){
		int len = hex.length()-1;
		//是否需要進位
		boolean b = true;
		//參與了運算的位,運算之後的結果字串,
		//由右向左運算,首先是最後一位參與運算,如果需要進位,那麼倒數第二位也要參與運算。以些類推
		String change = "";
		//最終運算結果,由未參與運算的位和參與了運算的位兩部分組成
		String result = "";
		while(b && len>=0){
			char c = hex.charAt(len);
			int index = indexOfCharArray(hexs, c);
			if(index == -1){
				return "所傳的字串引數非法!";
			}else{
				int sum = index+num;
				if(sum<16){
					change = hexs[sum]+change;
					result = hex.substring(0, len)+change;
					b = false;
				}else{
					change = hexs[sum%16]+change;
					num = sum/16;
				}
			}
			--len;
		}
		while(b){
			if(num<16){
				change = hexs[num]+change;
				result = change;
				b= false;
			}else{
				change = hexs[num%16]+change;
				num /= 16;
			}
		}
		
		return result;
	}
	/**
	 * 獲取字元在字元陣列中的下標
	 * 引數一:字元陣列
	 * 引數二:字元
	 * 返回值:字元在字元陣列中的下標,如果字元陣列中沒有該字元,返回-1
	 */
	public static int indexOfCharArray(char[] charArray, char c){
		for(int i=0, len=charArray.length; i<len; ++i){
			if(charArray[i]==c){
				return i;
			}
		}
		return -1;
	}
}

至此,方法就算封裝好了。新的問題又來了:字元大小寫的問題,自增的值為負數的情況,字串中存在分隔符,分隔符還不一樣的問題。有問題,得一個一個地解決,程式碼我就不貼了,我只把思路說一下,大家可以自己實現:

1. 字元大小寫的問題,好解決,方法內第一步,把傳進來的字串,先全部轉成小寫,處理完後,再根據需要轉換成大寫或是不轉。這個最好是統一返回小寫,是否轉成大寫,由方法的呼叫者決定。也想過在方法裡判斷是否是大小寫,但如果傳進來的字串中,沒有字母的情況下,就沒法判斷大小寫了。

2. 自增的值為負數或是為0的情況,為0的情況,好處理,字串原樣返回,字串為負數的話,實際上就是做減法。不過呢,做減法,比做加法要複雜那麼一丟丟,大家可以自己嘗試一下。

3. 字串中存在不一樣的分隔符的問題,我的解決方案就是把字串中0-9a-f這些字元的下標全部放到一個集合裡面,在集合中,取最後一個元素,作為下標去字串中取字元,做加法,運算之後,要進位的話,是進位到集合中倒數第二個元素作為下標的位置上,這樣就會把不是16進位制字元的所有字元都過濾掉。

其實還有一些小問題,那就是這些字串的長度都是固定的,比如,UUID不算分隔符的話,是32位,但是做加法之後,一直進位的話,不能弄出一個33位的UUID出來,那就大大的不妙了。還有傳的字串為空字串,或是為null的情況,這些是屬於異常情況,也要處理,這樣,程式的健壯性就會更好。

最後說一點,運用這個思想,還可以實現超大數字的加、減、乘、除,比如兩個long的乘法,就可以把兩個long轉成字串,再運用上面的邏輯,進行運算,然後得出一個表示數字的超長字串哦,大家有興趣,可以嘗試去實現一下。

 

相關文章