前几天面试遇到这个问题:在Java中如何将字符串转化为整数,当时too young too naive,随便回答了一下。今天跑去看Java源码中paresInt函数的写法,Oh my god!其实不看也能写出来,但是我觉得源码中的实现更好。下面贴出源码顺道分析一下:

     /* @param      s   the {@code String} containing the integer
* representation to be parsed
* @param radix the radix to be used while parsing {@code s}.
* @return the integer represented by the string argument in the
* specified radix.
* @exception NumberFormatException if the {@code String}
* does not contain a parsable {@code int}.
*/
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;
}

首先参数:1)第一个是String,表示需要被转化的字符串;2)第二个是进制,表示字符串需要当做什么进制的字符串去解析。

18-30行表示:如果是空字符串,或者进制低于能解析的最小进制(2)或者高于能解析的最大进制(36),则抛出异常;

接下来看40-51行:这里主要是根据第一个字符去判断字符串代表的数字是正的还是负的,通过flag negative标记。

剩余的部分比较复杂,先解释一下基本思想:取出字符串中的每一位字符,按照进制radix转化为数字,倘若不是数字,则返回值为-1,抛出异常。到这里都很好理解,包括39行的判断,都是很基本的。其实我一开始想的是,可以检测字符串的长度n,然后直接得出最后正数结果相应位置上的数字,大体算法如下:

 public static int parseInt(String s){
int result = 0; int length = s.length(); for(int index = 0; index < length; index ++){
int number = s.charAt(index) - '0';//获取字符代表的数字
result += number * Math.pow(10, length - index - 1);
} return result;
}

这里是简写,很多情况包括正负都没有考虑,并且默认是10进制,这是我的想法。但是我发现源码的想法并非如此,抽象出来大致如下:

 public static int parseInt(String s){
int result = 0; int length = s.length(); for(int index = 0; index < length; index ++){
int number = s.charAt(index) - '0';
result *= 10;
result += number;
} return result;
}

这样子写,减少了很多的乘法,原先在进位上需要做(1+n)n/2次乘法,后面则只需要n次,这是一次改进。

接着代码要解决的是另外一个很重要的问题,Java中整数值都是32位的,它是有范围的。我们需要验证字符串转化之后是不是在这个范围以内,即[Integer.MIN_VALUE, Integer.MAX_VALUE]。这就是59-65行要做的事情。

正数最大值可以达到2147483647,如果给出字符串“2147483648”,则解析出来肯定超范围。如何检测呢,根据上面的算法,假设解析到214748364,我们打断解析最后一位,可以通过Integer.MAX_VALUE-214748364 * radius <= 下一个digit来判断,如果表达式成立,则可以继续解析,否则不可以解析。但是这样想是有局限的,比如我们实际要解析的字符串是“89”,则可以看到其实上面那个表达式并不成立,但是89明显小于最大范围,可以解析,这里如何解决呢?

我们可以这样:将范围同时缩小一个量级,即解析出来的结果不去和2147483647比较,而是和214748364比较,当超出这个范围的时候,我们再使用上面的表达式进行判断。负数亦然。按照这个思想,我将上面的代码改了一下:

 public static int parseInt(String s){
int result = 0;
int limit = Integer.MAX_VALUE;
int upLimit = limit / 10; int length = s.length(); for(int index = 0; index < length; index ++){
int number = s.charAt(index) - '0'; if(result > upLimit)//这个时候乘以result * 10,必然大于Integer.MAX_VALUE
return -1; if(result == upLimit && (limit - result * 10) < number)
return -1; result *= 10;
result += number;
} return result;
}

注意代码中11行的注释。我使用几个边界数字测试的结果是正确的,这里同样默认是正数,最大只能解析到2147483647。Java的实现和这个就差不多了,但是Java奇怪的地方是在于使用减法而非加法,可以详细对比一下Java源码的66行和上面我的代码的第18行。Java的这种想法在其代码的第71行也有表现,我们可以看到,当值是负数的时候,直接返回result,否则是要取负数的。

Java源码在52行设置了multmin,59-61行代码和我的代码的11-12行作用一样,62-65行则和我代码的14-15行代码一样。但是我的代码这样写,是需要分类讨论的,即需要分为正负数去讨论。Java的精妙在于:将传入的字符串去掉正负号,根据正负设定下限,然后使用同一种方法去解析剩余的字符串,而可以不管正负!

在Java的源码中,如果传入的是正数,则下限是-Integer.MAX_VALUE,如果是负数,则下限是Integer.MIN_VALUE,然后使用negative去判断返回的时候是不是应该添加负号。中间则按照上面的思路,59-61行用来确保result * radix不会超出界限,62-65行则用来判断最终是否超出界限。注意,我的代码和Java的代码其实都注意到一点,Java代码中63行比较符合思维的写法应该是:

 if (result - digit < limit) {

但是Java并没有这样写,而是写成源码中的形式,我的代码14行也是如此,这里的主要原因是,这行代码本身就是检测result-digit是否超出界限的,如果按照上面的写法,result-digit如果超出界限,则会报错,但是按照Java源码的写法,limit+digit是肯定在表示范围内的!

另外,注意,这里并不存在统一设置上限的写法,因为-Integer.MIN_VALUE > Integer.MAX_VALUE!

【Java】将字符串转化为整数的更多相关文章

  1. 剑指offer 把字符串转化为整数

    题目描述 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数. 数值为0或者字符串不是一个合法的数值则返回0 输入描述: 输入一个字符串,包括数字字母符号,可以为空 输出描述: 如果是合法 ...

  2. Java实现字符串转换成整数

    1 问题描述 输入一个由数字组成的字符串,请把它转换成整数并输出.例如,输入字符串"123",输出整数123. 请写出一个函数实现该功能,不能使用库函数. 2 解决方案 解答本问题 ...

  3. Python:利用内建函数将字符串转化为整数

    使用内建函数raw_input()内建函数,它读取标准输入,并将读取到的数据赋值给指定的变量.我们可以使用int()内建函数将用户输入的字符串转换为整数: >>> user = ra ...

  4. JAVA日期字符串转化,日期加减

    SimpleDateFormat函数语法:  G 年代标志符  y 年  M 月  d 日  h 时 在上午或下午 (1~12)  H 时 在一天中 (0~23)  m 分  s 秒  S 毫秒  E ...

  5. 实现字符串转化为整数函数atoi()函数

    函数原型: int atoi(const char *nptr); 函数说明: 参数nptr字符串,如果第一个非空格字符存在,并且,如果不是数字也不是正负号则返回零,否则开始做类型转换,之后检测到非数 ...

  6. PHP中将字符串转化为整数(int) intval() printf()

    int <?php $foo = "1"; // $foo 是字符串类型 $bar = (int)$foo; // $bar 是整型 ?> intval <?ph ...

  7. 【Java】 剑指offer(67) 把字符串转换成整数

      本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 请你写一个函数StrToInt,实现把字符串转换成整数这个功能 ...

  8. Java将ip字符串转换成整数的代码

    下面代码是关于Java将ip字符串转换成整数的代码,希望对各位有较大用途. public class IpUtil { public static int Ip2Int(String strIp){ ...

  9. 算法笔记_028:字符串转换成整数(Java)

    1 问题描述 输入一个由数字组成的字符串,请把它转换成整数并输出.例如,输入字符串“123”,输出整数123. 请写出一个函数实现该功能,不能使用库函数. 2 解决方案 解答本问题的基本思路:从左至右 ...

随机推荐

  1. 软件工程第九周psp

    1.PSP表格 2.进度条 3.饼状图 4.折线图

  2. CodeForces 154A Hometask dp

    题目链接: http://codeforces.com/problemset/problem/154/A 题意: 给你一个字符串,和若干模板串(长度为2),至少删除多少个字母,使得字符串的字串里面没有 ...

  3. 01.1 Windows环境下JDK安装与环境变量配置详细的图文教程

    01.1 Windows环境下JDK安装与环境变量配置详细的图文教程 本节内容:JDK安装与环境变量配置 以下是详细步骤 一.准备工具: 1.JDK JDK 可以到官网下载 http://www.or ...

  4. 树形结构的数据库表Schema设计-基于左右值编码

    树形结构的数据库表Schema设计 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门.栏目结构.商品分类等等,通常而言,这些树状结构需要借助于数据库完 成持久化.然而目前的 ...

  5. Checkbox & Excel

    Checkbox & Excel Q: Excel how to check checkbox? 这个怎么打勾✔ ? A: 可以打勾的 How to Insert and Use a Chec ...

  6. 【bzoj4425】[Nwerc2015]Assigning Workstations分配工作站 贪心+堆

    题目描述 佩内洛普是新建立的超级计算机的管理员中的一员. 她的工作是分配工作站给到这里来运行他们的计算研究任务的研究人员. 佩内洛普非常懒惰,不喜欢为到达的研究者们解锁机器. 她可以从在她的办公桌远程 ...

  7. Innobackupx工具命令简单解析

    --defaults-file 同xtrabackup的--defaults-file参数,指定mysql配置文件; --apply-log 对xtrabackup的--prepare参数的封装; - ...

  8. Day21-自定义simple_tag/filter

    一. 在模板里面对用户发过来的数据进行二次加工.想办法把Python的函数放到模板语言里面使用. 有2种方法:filter与simple_tag 二. 实例 {{name|lower}} trunca ...

  9. 笔记-自己看Day20-待续

    1. 搭建环境 1)注释掉csrf 2)配置模板路径 'DIRS': [os.path.join(BASE_DIR,'templates')],  # BASE_DIR,代指当前目录. 3)配置静态文 ...

  10. 【刷题】洛谷 P3808 【模板】AC自动机(简单版)

    题目背景 这是一道简单的AC自动机模板题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 管理员提示:本题数据内有重复的单词,且重复单词应该计算多次, ...