JDK源碼解讀(第七彈:Integer之parseInt方法)

Integer的parseInt方法也是比較常用的方法,我們同樣重點來研究一下。看到源碼我們發現有兩個parseInt方法:

    public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }

        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }

    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }

可以看到,第二個方法只是調用了第一個方法,所以只要研究第一個parseInt即可。

第一個參數是要轉換的字符串,第二個參數是進制數。
一開始先進行幾個判斷,s不能爲空,進制數必須在2到36之間,如果不滿足則拋出異常。

下面的處理都是基於負數,因爲負數Integer.MIN_VALUE的絕對值比較大,而且全部基於負數處理在算法上比較統一。

接下來先取第一個字符,是數字還是正負號分別進行處理。

while循環中的代碼是核心處理邏輯,也就是把字符串轉換成數字。

我們先想一下,考慮一個三位數123,比較通常的算法是:
如果是十進制,123=110的2次方+210的1次方+310的0次方。
如果是八進制,123=1
8的2次方+28的1次方+38的0次方。
但仔細看while循環中的這兩行代碼,result *= radix;result -= digit;,我們發現他並不是使用上面的算法,而是使用了變種的算法:
如果是十進制,123=((1*10+2)10+3)。
如果是八進制,123=((1
8+2)*8+3)。
意思其實是一樣的,但是這種算法對於程序設計來說更加友好。

至於爲什麼是result -= digit;而不是result += digit;,是因爲這裏的計算全部基於負數,所以算法裏的加在這裏就是減。

我們還注意到,代碼裏定義了一個int multmin;,以及下面還有這行代碼:multmin = limit / radix;,以及循環裏的if (result < multmin)if (result < limit + digit),這又是幹什麼呢?
這是該方法的另一個精妙之處,它用來判斷是否溢出。每一次的循環都會判斷下一次循環的計算是否會溢出。
爲了便於理解,我們以十進制爲例,並且假設十進制的最大值爲998,
那麼定義的limit就是-998,根據multmin = limit / radix;這行代碼,multmin就是-99。
進入循環以後,假如result已經到了-100,if (result < multmin)會check出來,因爲在下一次循環的時候,result=-10010=-1000,溢出。
假如result爲-99,if (result < multmin)可以通過,但是如果digit=9,那麼if (result < limit + digit)會check出來,因爲在下一次循環的時候,result=-99
10-9=-999,溢出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章