最近看劍指 Offer,最后的面試案例有一道字符串轉整形的題,雖然題本身不難,但是需要考慮很多特殊情況,包括參數為空字符串、null 以及溢出等。
好奇看了一下 Java 源碼中 Integer 類的 parseInt() 方法的實現(valueOf() 方法調用了parseInt() 方法),發現這個實現對於溢出的處理很巧妙,故在此處記錄一下。
之前自己實現字符串轉整形的方法時,用 long 來表示最后轉換完的結果,然后再強轉成 int 型,這樣可以方便的處理溢出的情況。

// 部分參考了 Integer.parseInt() 的實現 static int strToInt(String str){ if (str == null || str.length() == 0) throw new NumberFormatException(str); long res = 0; boolean negative = false; int len = str.length(); int i = 0; char firstChar = str.charAt(0); if (firstChar < '0'){ if (firstChar == '-'){ negative = true; }else if (firstChar != '+'){ throw new NumberFormatException(str); } if (len == 1){ throw new NumberFormatException(str); } i++; } while(i < len){ char ch = str.charAt(i); if (ch < '0' || ch > '9'){ throw new NumberFormatException(str); } int digit = str.charAt(i++) - '0'; res = res * 10 + digit; if (negative == false && res > Integer.MAX_VALUE){ throw new NumberFormatException(str); } if (negative == true && res - 1 > Integer.MAX_VALUE){ throw new NumberFormatException(str); } } return negative ? -(int)res : (int)res; }
然后看到了 Integer.parseInt() 的實現,每次進行乘操作和加(這里是減)操作之前,都會先判斷一下最終結果的大小,如果有可能超出 int 的范圍,就不會執行乘或減的運算。

public static int parseInt(String s, int radix) throws NumberFormatException{ // 省略異常情況的判斷。。。 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; }
LeetCode 上有一道轉整形的題,允許更多的輸入情況。順便做了一下,代碼如下(14 ms,38.6 M):

public int myAtoi(String str) { if (str == null) return 0; long res = 0; boolean negative = false; int i = 0; str = str.trim(); if (str.length() == 0){ return 0; } int len = str.length(); char firstChar = str.charAt(0); if (firstChar == '-'){ negative = true; i++; }else if (firstChar == '+'){ i++; } while(i < len){ char ch = str.charAt(i); if (ch < '0' || ch > '9'){ return negative ? -(int)res : (int)res; } int digit = str.charAt(i++) - '0'; res = res * 10 + digit; if (negative == false && res > Integer.MAX_VALUE){ return Integer.MAX_VALUE; } if (negative == true && res - 1 > Integer.MAX_VALUE){ return Integer.MIN_VALUE; } } return negative ? -(int)res : (int)res; }