输入一个表示整数的字符串,把该字符串转换成整数并输出。 例如输入字符串"345",则输出整数 345。
这个面试题不太 涉及算法和数据结构,主要考察你的编程习惯,有没有意识去做一些验证,对输入的校验。小问题验证大方面。乘机我们来看看java api中对这个问题的处理
public static int parseInt(String s) throws NumberFormatException { return parseInt(s,10); } 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; }
先了解下面的知识,对理解上面的代码有好处。
在Java中>、>>、>>>三者的区别 在java中: >表示大于,如:if(a>b)...结果是boolean类型 >>表示右移,如:int i=15; i>>2的结果是3,移出的部分将被抛弃。 转为二进制的形式可能更好理解,0000 1111(15)右移2位的结果是0000 0011(3),0001 1010(18)右移3位的结果是0000 0011(3)。 j>>>i 与 j/(int)(Math.pow(2,i))的结果相同,其中i和j是整形。
我们一步一步的debug, 来看看处理代码
public static void main(String[] args){ //10进制 Integer.parseInt("235",10); }
上面是入口函数。
传入的字符串为"235"。进入方法体后,第一步是判断是否为null。如果为空,抛出异常。然后是判断基数是否在可允许范围内[2-32],我们传入的是10。在可允许内
result:我们需要的结果
negative:是否为负数。默认为false
i:游标,指向字符串当前的索引,遍历字符串中字符。
len:字符串大小
limit:整形的最大值的负值。比整形的最小值-2147483648大1.
multmin:
digit:保存字符经过转化后的整形值
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++; }
如果第一个字符不为‘-’,然后再附加判断一下是否为加号,如果不是加号,抛出异常
再然后判断一下字符串的大小是否为1,如果为1,不符合要求,因为只有符号位肯定是不复制整数的定义的。
最后字符串遍历的游标自增1。表示遍历要从第二个字符开始,因为第一个字符我们已经判断是符号位。
首先我们将multmin的值赋上值,赋上值有啥作用?,它为啥要除以基准值。暂时还不清楚。先放着吧。
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; }
digit = Character.digit(s.charAt(i++),radix);
可以看见字符'2'的ascii码为50,我们对照一下的确是50。
上面的方法中,首先将字符转换成ascii码,然后再调用覆盖函数digit。
这个时候会发现传进来的是50,也就是‘2’的ascii码值。进入该函数后,首先通过CharacterData.of方法来得到该字符属于哪个编码集范围内。
static final CharacterData of(int ch) { if (ch >>> 8 == 0) { // fast-path return CharacterDataLatin1.instance; } else { switch(ch >>> 16) { //plane 00-16 case(0): return CharacterData00.instance; case(1): return CharacterData01.instance; case(2): return CharacterData02.instance; case(14): return CharacterData0E.instance; case(15): // Private Use case(16): // Private Use return CharacterDataPrivateUse.instance; default: return CharacterDataUndefined.instance; } } }
进入CharacterDataLatin1类的digit方法
int digit(int ch, int radix) { int value = -1; if (radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX) { int val = getProperties(ch); int kind = val & 0x1F; if (kind == Character.DECIMAL_DIGIT_NUMBER) { value = ch + ((val & 0x3E0) >> 5) & 0x1F; } else if ((val & 0xC00) == 0x00000C00) { // Java supradecimal digit value = (ch + ((val & 0x3E0) >> 5) & 0x1F) + 10; } } return (value < radix) ? value : -1; }
int getProperties(int ch) { char offset = (char)ch; int props = A[offset]; return props; }
为何要做位与运算,先来看看位于运算是什么?
& 是按二进制的按位与,即 1 & 1 = 1 1 & 0 = 0 3 & 1 → 11(二进制) & 1 = 1
10进制数402667017转化为二进制数为
11000000000000011011000001001
if (kind == Character.DECIMAL_DIGIT_NUMBER)
value = ch + ((val & 0x3E0) >> 5) & 0x1F;
0x3E0 = 10 1111 0000 &10 0000 1001 = 10 0000 0000 然后向右位移5位得到10 000由于优先级+大于&。所以先进行相加运算50+16 = 66 (1000010)
1000010&11111 = 10 =,10进制就为2;所以最后的value = 2;
所以方法返回到Integer.parseInt中,得到digit = 2;
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; }
后面一个判断result<multmin是什么意思呢。因为result即将*10。如果result<multmin,那么再*10的话肯定超出最小的范围。这个地方为什么不能直接先乘后比较,先乘后比较就没有意义了,如果超出了范围,已经报错了,后面的判断也就不会执行。接下来的一句的判断同样是这个道理,因为result即将与digit相减,但是相减的前提是它不能越界。所以有个预先判断。