Java String 转整形
最近看剑指 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; }