首頁 > 軟體

java中int轉string與string轉int的效率對比

2022-03-11 19:00:45

int轉string與string轉int的效率對比

string轉int,兩種方法

Interger.parseInt(String)
Interger.valueOf(String).intValue()

第二種方法可以去看原始碼,實現了第一種方法。

註釋大概就是這樣的意思

/**
      *返回一個包含整數的物件
      *指定的{@ String String}的值。 這個說法是
      *被解釋為表示一個有符號的十進位制整數
      *就好像這個論據是給予{@link的
      * #parseInt(java.lang.String)}方法。 結果是一個
      表示整數值的整數物件
      *由字串指定。
     *
      換句話說,這個方法返回一個{@code Integer}
      *物件等於以下值:
     *
      * <blockquote>
      * {@code new Integer(Integer.parseInt(s))}
      * </ blockquote>
     *
      * @param是要解析的字串。
      * @返回一個儲存值的{整數}物件
      *由字串參數列示。
      * @exception NumberFormatException如果字串不能被解析
      *作為一個整數。
     */

在valueOf()裡面實現了parseInt()方法。時間對比第二種比第一種要快了很多。

 Integer.parseInt(str) : 21
 Integer.valueOf(str).intValue() : 14

int 轉string一般用三種方法

  • 第一種:number + ""
  • 第二種:string.valueOf()
  • 第三種:.toString()
  • 先說第一種,簡單粗暴。
  • 第二種方法:底層使用的依舊是.toString()方法
  • 第三種就是toString()

上程式碼。

int num = 888888;
 
        //(1)num + ""
        long start = System.currentTimeMillis();//得到開始執行時系統時間
        for(int i=0; i<100000; i++){
            String str = num + "";
        }
        long end = System.currentTimeMillis();//得到結束執行時系統時間
        System.out.println("num + "" : " + (end - start));
 
        //(2)String.valueOf(num)
        start = System.currentTimeMillis();
        for(int i=0; i<100000; i++){
            String str = String.valueOf(num);
        }
        end = System.currentTimeMillis();
        System.out.println("String.valueOf(num) : " + (end - start));
 
        //(3)Integer.toString(num)
        start = System.currentTimeMillis();
        for(int i=0; i<100000; i++){
            String str = Integer.toString(num);
        }
        end = System.currentTimeMillis();
        System.out.println("Integer.toString(num) : " + (end - start));

結果就是

num + "" : 82
String.valueOf(num) : 32
Integer.toString(num) : 9

經過多次的反覆測試,toString()是最快的,num+""是最慢的,在使用String.valueOf()中原始碼是這樣的。

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

也就是說在使用的時候,不用去判斷所傳的物件是否為null,但是尤其注意,如果傳的為空,返回來的是一個為null的字串而不是null值,這個地方需要謹記。

string轉int問題分析

相信很多同學在面試時都遇到過這樣一個問題,要求封裝一個函數,將String型別轉換為int型別。這個看似簡單的問題其實隱藏著很多細節,要想真正封裝好這個函數並不容易。面試官要考察的其實並不是演演算法本身的難度,這個問題的演演算法其實沒有什麼難度可言,主要要考察的是程式設計師寫程式碼的仔細程度,考慮問題是否全面,也就是說,我們要儘可能的讓程式碼具有魯棒性。下面我們一步步的分析這個問題中隱藏的細節。

分析一波

首先我們不考慮任何的例外處理,假設函數的呼叫者傳入的資料都是正確的,很容易就可以寫出下面的程式碼:

    public int strToInt(String str) {
        int number = 0;
        for (int i=0; i<str.length(); i++) {
            number *= 10;
            number += (str.charAt(i) - '0');
        }
        return number;
    }

上面的程式碼將遍歷字串的每一位字元,並將其轉換為對應的整數,然後將其一一融入到整形資料number中。

如果你給面試官提交的是這樣一份程式碼,結果肯定不會滿意。因為你沒有考慮到程式的魯棒性,我們封裝的函數相當於API介面,是提供給所有開發者呼叫的,難免其他開發者不會傳入一些奇怪的引數,而這段程式碼對異常引數沒有做任何處理,一旦傳入異常引數,程式將直接崩潰。下面我們一步步來完善這個函數,提高其魯棒性。

1、針對傳入的字串為空物件或者字串為空的字串

    public int strToInt(String str) throws NumberFormatException{
        if (str == null || str.contentEquals("")) { // 如果傳入的字串為空物件或者傳入的字串為空字串,則丟擲異常
            throw new NumberFormatException("null or empty string"); // 這裡直接利用java封裝好的異常類,當然我們也可以自己封裝異常類,面試官要考察的不是對異常類的封裝,而是你要知道要處理異常情況
        }
        int number = 0;
        for (int i=0; i<str.length(); i++) {
            number *= 10;
            number += (str.charAt(i) - '0');
        }
        return number;
    }

首先我們字串是否為空或者是否為空的字串,如果是,則直接丟擲異常,這裡我們使用的是Java封裝好的異常類NumberFormatException,當然我們也可以自己封裝異常類,面試官要考察的不是對異常類的封裝,而是你要知道要處理異常情況。

2、針對符號位的處理

這個我們最好提前問一下面試官,有沒有可能傳入的是負數,當為正數時,是否允許帶符號位,如果是的話,我們就要針對符號位進行處理,負數的第一個字元是“-”,我們只要判斷第一個字元是否為“-”就可以知道傳入的是否為負數了,如果正數允許帶符號位,那邊第一個字元有可能是“+”,我們也要做對應的處理:

    public int strToInt(String str) throws NumberFormatException{
        if (str == null || str.contentEquals("")) { // 如果傳入的字串為空物件或者傳入的字串為空字串,則丟擲異常
            throw new NumberFormatException("null or empty string"); // 這裡直接利用java封裝好的異常類,當然我們也可以自己封裝異常類,面試官要考察的不是對異常類的封裝,而是你要知道要處理異常情況
        }
        boolean negative = false; // negative為true表示是負數,反之為正數
        int pos = 0;
        if (str.charAt(0) == '-') { // 如果為負數
            negative = true;
            pos++; // 調過第一位符號位
        } else if (str.charAt(0) == '+') {
            pos++; // 調過第一位符號位
        }
        int number = 0;
        while (pos < str.length()) {
            number *= 10;
            number += (str.charAt(pos) - '0');
            pos++;
        } 
        return negative ? -number : number; // 如果為負數則返回對應的負數
    }

3、針對錯誤字元的處理

函數的呼叫者可能會傳入一下亂七八糟的字串,比如“abc23123”,針對這種情況我們也要做對應的處理,應該給呼叫者丟擲一個異常,告知其傳入的字串是非法字串:

    public int strToInt(String str) throws NumberFormatException{
        if (str == null || str.contentEquals("")) { // 如果傳入的字串為空物件或者傳入的字串為空字串,則丟擲異常
            throw new NumberFormatException("null or empty string"); // 這裡直接利用java封裝好的異常類,當然我們也可以自己封裝異常類,面試官要考察的不是對異常類的封裝,而是你要知道要處理異常情況
        }
        boolean negative = false; // negative為true表示是負數,反之為正數
        int pos = 0;
        if (str.charAt(0) == '-') { // 如果為負數
            negative = true;
            pos++; // 調過第一位符號位
        } else if (str.charAt(0) == '+') {
            pos++; // 調過第一位符號位
        }
        int number = 0;
        while (pos < str.length()) {
            if (str.charAt(pos) >= '0' && str.charAt(pos) <= '9') { // 只有字元在'0'到'9'的範圍內,才算正確的字元
                number *= 10;
                number += (str.charAt(pos) - '0');
                pos++; 
            } else {
                throw new NumberFormatException("invalid string"); // 當字元是其他字元時,丟擲異常告知呼叫者傳入的字串錯誤
            }
        } 
        return negative ? -number : number; // 如果為負數則返回對應的負數
    }

4、針對整形資料超出範圍的處理

呼叫者傳入的字串可能是一個很長的字串,轉換為整數可能超出了整數的儲存範圍,比如“12345678674324334”,在這種情況下,我們要丟擲一個異常告知呼叫者傳入的字串超出了整形的範圍:

    public int strToInt(String str) throws NumberFormatException{
        if (str == null || str.contentEquals("")) { // 如果傳入的字串為空物件或者傳入的字串為空字串,則丟擲異常
            throw new NumberFormatException("null or empty string"); // 這裡直接利用java封裝好的異常類,當然我們也可以自己封裝異常類,面試官要考察的不是對異常類的封裝,而是你要知道要處理異常情況
        }
        boolean negative = false; // negative為true表示是負數,反之為正數
        int pos = 0;
        if (str.charAt(0) == '-') { // 如果為負數
            negative = true;
            pos++; // 調過第一位符號位
        } else if (str.charAt(0) == '+') {
            pos++; // 調過第一位符號位
        }
        int limit = negative ? (-Integer.MIN_VALUE) : Integer.MAX_VALUE;
        int mult = limit / 10; // 記錄最大數/10,讓number和這個數比較,如果大於它,則number * 10肯定也就大於最大數
        int number = 0;
        while (pos < str.length()) {
            if (str.charAt(pos) >= '0' && str.charAt(pos) <= '9') { // 只有字元在'0'到'9'的範圍內,才算正確的字元
                if (number > mult) {// 讓number和mult比較,如果大於它,則number * 10肯定也就大於最大數
                    throw new NumberFormatException("input string beyond int size");
                }
                number *= 10;
                int digit = str.charAt(pos) - '0';
                if (number > limit - digit) { // 這裡不能用number + digit > limit來判斷,因為number + digit可能超出整數的儲存範圍,相加後的數可能是一個負數,但是limit - digit肯定不會超出
                    throw new NumberFormatException("input string beyond int size");
                } else {
                    number += digit;
                }
                pos++;
            } else {
                throw new NumberFormatException("invalid string"); // 當字元是其他字元時,丟擲異常告知呼叫者傳入的字串錯誤
            }
        } 
        return negative ? -number : number; // 如果為負數則返回對應的負數
    }

上面的程式碼中,我們判斷number是否會超出最大整數時首先是先讓其(最大整數/10)的值比較,而不是讓其乘以10與最大整數比較,這是因為number * 10如果超出了整數範圍,則會造成資料溢位,其得到的值可能是一個負數,而(最大整數/10)的值是不會資料溢位的,這也是一個小細節。可能你以為這樣這個函數就完美了,但是現在我要告訴你,上面的寫法是錯誤的。

為什麼呢?這要從整數的範圍說起,整數的取值範圍是(-2^31)至(2^31 - 1),從絕對值的角度看,最小負數相比於最大正數大1。所以上面程式碼中(-Integer.MIN_VALUE)會超出整形的範圍,造成資料溢位,也就是說上面的程式碼對負數最小範圍的限制的處理是錯誤的。那麼怎麼解決這個問題呢?

我們換個角度思考,最小負數的絕對值比最大正數的絕對值大1,那(-Integer.MAX_VALUE)的值肯定不會超出整數的範圍,我們現在的程式是以正數的方式處理,如果反過來已負數的方式處理,問題不就解決了嗎?修改程式碼如下:

    public int strToInt(String str) throws NumberFormatException{
        if (str == null || str.contentEquals("")) { // 如果傳入的字串為空物件或者傳入的字串為空字串,則丟擲異常
            throw new NumberFormatException("null or empty string"); // 這裡直接利用java封裝好的異常類,當然我們也可以自己封裝異常類,面試官要考察的不是對異常類的封裝,而是你要知道要處理異常情況
        }
        boolean negative = false; // negative為true表示是負數,反之為正數
        int pos = 0;
        if (str.charAt(0) == '-') { // 如果為負數
            negative = true;
            pos++; // 調過第一位符號位
        } else if (str.charAt(0) == '+') {
            pos++; // 調過第一位符號位
        }
        int limit = negative ? Integer.MIN_VALUE : (-Integer.MAX_VALUE);
        int mult = limit / 10;
        int number = 0;
        while (pos < str.length()) {
            if (str.charAt(pos) >= '0' && str.charAt(pos) <= '9') { // 只有字元在'0'到'9'的範圍內,才算正確的字元
                if (number < mult) {
                    throw new NumberFormatException("input string beyond int size");
                }
                number *= 10;
                int digit = str.charAt(pos) - '0';
                if (number < limit + digit) {
                    throw new NumberFormatException("input string beyond int size");
                } else {
                    number -= digit;
                }
                pos++;
            } else {
                throw new NumberFormatException("invalid string"); // 當字元是其他字元時,丟擲異常告知呼叫者傳入的字串錯誤
            }
        } 
        return negative ? number : -number;
    }

OK,現在我們把能夠想到的異常情況處理了。再來考慮一個問題,為什麼整形資料的範圍是(-2^31)至(2^31 - 1),最小負數的絕對值比最大正數的絕對值要大1呢?

5、int資料範圍的討論

我們知道,一個int型別佔四個位元組,也就是32位元,其中第一位是符號位,符號位為0表示正數,為1表示負數,其餘31位元表示數值。正常來說int型別的資料範圍應該是(-2^31-1)到(2^31-1),為什麼負數會多一位呢?

我們首先看一下Java程式碼中對Integer.MAX_VALUE和Integer.MIN_VALUE的定義:

    /**
     * A constant holding the minimum value an {@code int} can
     * have, -2<sup>31</sup>.
     */
    public static final int   MIN_VALUE = 0x80000000;
 
    /**
     * A constant holding the maximum value an {@code int} can
     * have, 2<sup>31</sup>-1.
     */
    public static final int   MAX_VALUE = 0x7fffffff;

原碼、反碼、二補數

我們知道,在計算機中,資料都是以二進位制的形式儲存的,比如,數位10,其二進位制形式就是1010。

一個位元組有8位元,每位可以儲存一個01字元,byte型別佔1個位元組,也就是8位元,其中,最高位是符號位,用來表示數值是正數還是負數,符號位為0表示正數,符號位為1表示負數。我們先來看一下原碼、反碼、二補數的定義:

  • 原碼:符號位加上真值的絕對值, 即用第一位表示符號, 其餘位表示值。
  • 反碼:正數的反碼是其本身;負數的反碼是在其原碼的基礎上, 符號位不變,其餘各個位取反。
  • 二補數:二補數的表示方法是:正數的二補數就是其本身;負數的二補數是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1。 (即在反碼的基礎上+1)

正數的原碼、反碼、二補數都是其本身;負數的反碼是在其原碼的基礎上,符號位不變,其餘個位取反,負數的二補數是其反碼的基礎上+1。

舉例說明(下面都以byte型別進行舉例):

資料原碼反碼二補數
10000010100000101000001010
-10100010101111010111110110

計算機中,資料都是以二補數的形式儲存的。為什麼要以二補數的形式儲存呢?有兩個原因:

1、如果數值以二補數的形式儲存,對一個數進行求補運算,可以得到其相反值

求補運算:將一個數(包括正數和負數)所有二進位制位(包括符號位和數值位)取反,然後在最低位加上1。

為什麼對一個數進行求補運算,可以得到其相反值呢?我們先來分析一下求補運算的定義,現將所有的二進位制取反,然後+1,首先一個數和它所有位取反得到的數相加,其結果肯定是11111111,這是因為它們每一位都不一樣,然後將結果+1,即11111111 + 1,結果是1 00000000,最高位的1已經溢位,換種方式說,如果以f(n)表示對n進行求補運算,那麼對於任意的範圍內的數,可以得到:

n + f(n) = 1 00000000

f(n) = 1 00000000 - n

而對於一個正數來說,對其進行求補運算其實得到的就是它的相反數的二補數(負數的二補數符號位保持不變,其他為全部取反再+1,因為正數和負數的符號位本來就不一樣,所以對一個正數進行求補其實得到的就是它的相反數的二補數)。

那麼對於一個負數來說呢?對其進行求補運算是否能夠得到其對應的正數的二補數呢?

假設n>0,根據上面可知:

f(n) = 1 00000000 - n

對f(n)進行求補運算,有:

f(f(n)) = f(1 00000000 - n) = 1 00000000 - (1 00000000 - n) = n

其中,1 00000000 - n表示n對應負數的二補數,對其進行求補運算得到的就是n,正數的二補數就是其原碼。

由上可知:如果數值以二補數的形式儲存,對一個數進行求補運算,可以得到其相反值,即:f(n) = -n

2、方便減法運算

如果數值以二補數的方式儲存,可以將減法變為加法,省去了減法器,通過上面的推導,如果資料以二補數的方式儲存,以f(n)表示對n進行求補運算,可以得到:

f(n) = -n

那麼現在我們需要計算m - n,應該要怎麼計算呢?如果以二補數的方式儲存,那麼就有:

m - n = m + (-n) = m + f(n)

也就是說,減去一個數只需要加上對其進行求補運算後得到的值即可。

3、使用二補數儲存資料,可以對任意的兩個數直接進行相加運算,不用考慮符號位

4、通過二補數形式儲存,規定10000000對應的負數的最小值,也就是-128。

由上面可知,如果是byte型別,資料範圍應該為[-128,127],最小負數要比最大正數多一位。

小結一下

我們在寫程式碼時,不能僅僅注意程式碼的功能,還要考慮到程式碼的魯棒性,比如引數的正確性、引數的範圍以及異常情況的處理等等。只有這樣,我們寫出的程式才能更加健壯,這也是一個優秀開發者的基本素質。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com