刷leetcode正好刷到了字符串转int的题目,就看了看java源码是如何实现的,借鉴一下思路。
核心是parseInt(),至于Integer.valueOf()只是做了一个[-128,127]的常量池。
全部代码粘贴如下,去除了s空串与radix非法范围的异常校验。
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;
}
整体思路也好理解,字符串从左到右扫描,result从0开始,每读取1位新字符,等于进位一次,所以乘以进制。再减去digit。
举例说明读取“123”,result变化是0,-1,-12,-123。
这里主要聊聊result为什么采用减法而不是加法计算,以及边界值判定。
1.result采用减法而不是加法计算,就是为了让result变成一个负数。因为int是有符号数字,范围是[-2^31~2^31-1]。负数比正数多一位。如果result用正数存储,那么就不能存储2^31。也就没办法表示-2^31。
2.边界值判定
边界值判定依靠两个值,一个是limit,另一个是multmin。首先是limit选取,正数范围应该是2^31-1。因为计算过程变成负数,所以这里取-(2^31-1)。负数是-2^31,不用变。这里还有个注意点是比较顺序,result < limit + digit 的判断应该放在result -= digit;之前,否则运算完就直接溢出,判断不出来了。然后是multmin。为什么在limit之外还需要判断,因为result *= radix;也可能导致溢出。multmin是limit右移一位的值。这里直接举数字说明。int最多能存储-2147483648。如果出现-5555555555就不行。反向思维一下,最后一位没有读取前int能接收最大数字要在[-214748364,0]之间。至于-2147483649的合法性问题就是limit来判定的。同样,multmin的判断也要在result *= radix运算前面。