技术

Integer-parstInt源码解读

This post is currently available in Chinese only. The original text follows.Open the Chinese page →

parseInt(String s)

java的parseInt函数可以将传入的字符串转换成数字,如果传入的字符串不满足要求,例如转换后的值过大,符号位过多,或者包含字母,则会进行异常的抛出

默认的parseInt是会调用一个可以传入进制数的函数

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

其中radix是进制,传入的radix为10就是说按照十进制的方式来计算传入的string的类型

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.
         */

//如果传入null,或者传入的进制不满足要求,直接抛出异常
//支持的进制范围是 2~36

        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++;
            }
					//根据最大值,以及进制数,计算出
					//如果为正数,limit为-Integer.MAX_VALUE
					//如果为负数,limit为Integer.MIN_VALUE,也就是说limit一直为负值
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
								//获取到数字的值,如果小于0则抛出异常,如果小于
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
								//因为result一直为正值,如果小于multmin说明有异常
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
								//进位
                result *= radix;
								//如果result<limit+digit 也就是result-digit < limit,说明越界了
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
								//倒着计算值
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
				//如果为符号为负,直接返回,否则取反,因为result是倒着计算的,取反之后为正数
        return negative ? result : -result;
    }

解读

  1. 首先判断首位是否为符号位,如果是符号位,则记录符号值,并更新limit值。

  2. limit始终为负值,如果符号位为负,则limit为Integer.MIN_VALUE,如果为正,则limit为Integer.MAX_VALUE的负值

  3. 计算的时候,会利用到multmin,主要是用来判断进位后的结果是否超过了limit。

    $mulmin = limit/radix$

    $mulmin*radix = limit$

    所以如果$result < mulmin$,也就等价于 $result * radix < limit$,也就是越界了,直接抛出异常

  4. 因为Integer.MAX_VALUE 和Integer.MIN_VALUE 不同,分开计算区间会比较麻烦,所以上面的做法是全部转为计算负值,进而是采用减法操作。因为抛开符号位来说,数值的计算方法是一样的,只是区间不同。比如是312,在进行计算的时候,会先计算出来-3,然后计算出来-31,再计算出来-312。然后取反,得到312

  5. 因为ASCII码中数字只有0-9,所以只需要判断char是否在当前范围即可。如果要处理的char是数字,则再进行下一步计算