Guawa中tryParse的研究

今天发现项目中在转化string为int时使用了guawa的Ints.tryParse方法。之前没有见过这样的用法,好奇,对其相关知识进行了研究。

为什么要使用tryParse

String转Integer,JDK本身就提供了响应的方法。就是Integer.parseInt(String s),那为什么还要再造轮子呢,因为JDK提供的这个方法抛一个NumberFormatException,由于这样,每次调用这个方法就必须用try catch来捕获这个异常,或者是再向上抛给上层的方法。
为什么需要这个异常
可以看到,这个方法接受的参数是个String, 如果传入的参数不是数字,或者格式非法,如传入abc,这样的字符串是无法转换成数字的,所以需要抛出这样的异常。
这样有什么不好
这样做不是正确的吗,有什么缺点呢?要知道,抛异常代价是很大的,详细原因可以看这里。总结一下就是:

  1. 抛异常必须生成stacktrace。生成过程消耗资源
  2. 异常处理需要特殊的流程控制。
  3. 异常处理消耗时间无法量化。原因:异常处理与stacktrace深度有关,如果stracktrace深度很深,会很消耗资源。
    所以,如果使用这样的方法,在非法字符串很多的时候,系统抛很多异常是很影响性能的事情。
    还有一点:程序员写程序,使用这样的方法会很影响程序的鲁棒性。
    所以,需要重新造一个更好的轮子,来解决这个问题。

    解决方案

    那么,一定要像JDK这样解决String转Integer的问题吗?答案当然是否定的。
    JDK的实现方式是使用抛异常的方式来解决字符串非法的问题。判断非法字符的方法有很多,不一定用抛异常的方式啊,这种方式是最不好的。
    方法详见链接
    总结下:
  4. 遍历string确定每个字符都是数字
  5. 使用正则表达式扫描
  6. 使用NumberFormat进行扫描
  7. 使用java.util.Scanner进行扫描

在Guawa中使用的是方案1,在将字符转化为数字的过程中使用了JDK的Character.digit方法,实现原理如下:

public static Integer tryParse(String string) {
return AndroidInteger.tryParse(string, 10);
}

首先调用了guawa自己的AndroidInteger中的tryParse方法,这个方法的第二个参数是基数。基数是什么呢,这样说吧,在这里传入10就会返回string的10进制对应的int值,如果传入的是2或者16则会返回二进制或者16进制对应的int值,例如AndroidInteger.tryParse('F',16)则会返回数值15.
AndroidInteger的tryParse源码:

static Integer tryParse(String string, int radix) {
checkNotNull(string);
checkArgument(radix >= Character.MIN_RADIX,
“Invalid radix %s, min radix is %s”, radix, Character.MIN_RADIX);
checkArgument(radix <= Character.MAX_RADIX,
“Invalid radix %s, max radix is %s”, radix, Character.MAX_RADIX);
int length = string.length(), i = 0;
if (length == 0) {
return null;
}
boolean negative = string.charAt(i) == ‘-‘;
if (negative && ++i == length) {
return null;
}
return tryParse(string, i, radix, negative);
}

可以看到,这里主要先是判断输入参数的合法性(checkArgument方法),再判断数字的符号(boolean negative = string.charAt(i) == '-';),剩下的事情都交给了return中的tryParse去做。

下面是return中tryParse代码:

private static Integer tryParse(String string, int offset, int radix,
boolean negative) {
int max = Integer.MIN_VALUE / radix;
int result = 0, length = string.length();
while (offset < length) {
int digit = Character.digit(string.charAt(offset++), radix);
if (digit == -1) {
return null;
}
if (max > result) {
return null;
}
int next = result * radix - digit;
if (next > result) {
return null;
}
result = next;
}
if (!negative) {
result = -result;
if (result < 0) {
return null;
}
}

这里可以看到,该方法主要使用了Character.digit(string.charAt(offset++), radix)将字符转化为int,并判断输入字符是否合法。将字符转化成int后int next = result * radix - digit;将原数值还原。最后判断正负号,然后返回值。
至此,实现原理大白于天下。

详细用法点此处)了解。值得一提的是,如果传入的string为带正号,如String s = "+12345"会被拒绝,认为是非法字符。

##总结
JDK自带的Integer.parseInt()方法会抛异常,抛异常影响程序的性能。所以Guawa重新造轮子,有了tryParse方法。原理为扫描字符串中字符,判断是否为数字。