【视频】自己实现字符串转整数JavaEE面试题-学不死的程序员
在面试Java研发工程师的时候,面试官让现场手写一个算法实现字符串转成整数,要求不能使用JDK提供的字符串转整数的方法,比如不能使用Integer.valueOf()方法和Integer.parseInt()方法。
下面通过一个视频来说说当时我实现的思路,视频长度约30分钟,建议在wifi环境下观看:
不方便观看视频的网友,可以阅读下面的文章恐怖休息站 ,和视频内容是一样的。
基本思路
字符串-->字符-->整数
就是先将字符串转成字符数组,然后通过遍历将每个字符转成int类型,最后将每个字符对应的整数拼装成最终的整数圣人请卸妆 。
比如,要将"3256"这个字符串转成整数,可以通过调用String的toCharArray()方法将其转成字符数组:
['3', '2', '5', '6']
然后再遍历该字符数组金马岛战役 ,将每一个字符转成int类型,从而得到每个字符对应的整数:
3孟庭苇老公 , 2, 5, 6
最后将每个字符对应的整数拼装起来,最终得到如下整数:
3256
后面会详细讲解该思路的实现细节。
异常处理
由于接收的是一个字符串,输入的字符串可能会出现以下几种异常的情况:
(1)null,空字符,非数字;
String str = null;
String str = "";//空字符
String str = " ";//空格
String str = "abc";
这几种情况都是非法的输入,当然要抛出异常,null值和空字符以及空格的情况好处理吸铁石邮箱,非数字的情况我们可以通过如下判断来排除('0'~'9'之外的字符自然都不是数字):
chars[i] < '0' || chars[i] > '9'
(2)+或-号;
String str = "+";
String str = "-";
String str = "+3256";
String str = "-3256";
输入的字符串允许以"+"号或"-"号开头,但是如果输入的是单独的"+"号或者"-"号,也要抛出异常。
关于负数的情况,我们会用一个变量negative来标记,如果输入的字符串以"-"号开头,说明是负数,标记为true,否则就是正数(默认值false表示正数):
boolean negative = false;
if (str.startsWith("-")) negative = true;
符号位只是用变量记录一下,运算的时候不会考虑符号问题,如果有符号位,就使用截取函数把符号位排除掉(截取首字符后面的部分):
str = str.substring(1);
排除掉符号位之后,如果字符串还是空的,说明输入的"+"或者"-"号后面没有数字,属于异常情况:
if (str == "" || str.length() == 0)
(3)溢出;
String str = "2147483648";//超出int最大值
String str = "-2147483649";//超出int最小值
输入的字符串可能会超出int的最大值或最小值,int的最大值是2147483647,int的最小值是-2147483648。溢出的情况也要抛出异常。
如果输入的是负数,并且超出了int的最小值,就通过如下判断给出异常提示:
negative&&-result<Integer.MIN_VALUE
如果输入的不是负数,并且超出了int的最大值,就通过如下判断给出异常提示:
!negative&&result>Integer.MAX_VALUE
解决了以上几种异常的情况,其他的输入就是合法的数字了,就可以走下面的逻辑:
字符串转成字符数组
假定输入的字符串是"3256":
String str = "3256";
调用String的toCharArray()方法将字符串转成字符数组:
char[] chars = str.toCharArray();
得到的字符数组为:
['3', '2', '5', '6']
字符转成整数
我们知道,键盘上的每一个字符都对应着一个整型的ASCII码,比如从'0'~'9'这个10个字符对应的ASCII码为:
48, 49, 50, 51包公奇案, 52, 53新堂爱 , 54, 55, 56, 57
我们不可能知道哪个字符对应的ASCII码是多少,但是我们只要知道它们是从小到大顺序排列的,相邻的两个数字,后面的比前面的大1,只要懂得这个规律,就可以通过如下方式来将单个字符转成它所对应的数字:
chars[i] - '0'
chars[i]可以拿到字符数组中的每一个字符,让它与字符'0'相减就能得到该字符对应的int类型的数字(字符之间的算术运算,其实就是其对应的ASCII码值之间的运算),比如:
'0' - '0' = 0
'1' - '0' = 1
'2' - '0' = 2
'3' - '0' = 3
……
'9' - '0' = 9
拼接整数
将每个字符转成了int类型,下面就可以将每一个int类型的数字组装起来,得到最终的整数。
我们知道,曹婴数字是有低位和高位的概念陈怡川,比如3256(三千二百五十六),它包含千位、百位、十位和个位,可以拆分成下面的样子的:
3256 = 3*1000 + 2*100 + 5*10 + 6
也就相当于:
3256 = 3*10^3 + 2*10^2 + 5*10^1 + 6*10^0
怎么知道哪个数字应该乘以10的几次幂呢潘南奎?看下面的示意图:
在遍历的时候,索引是递增的,幂是递减的七微 ,根据上图所示,幂和索引之间的关系是有规律可循的,即幂等于字符数组的长度减去索引,再减去1,可以使用一个求幂函数来计算幂:
Math.pow(10, chars.length - i - 1)
然后在循环遍历字符数组的时候,通过累加求和的方式拼装整数:
result += (chars[i] - '0') * Math.pow(10, chars.length - i - 1)
有了上面的详细分析,代码实现起来就简单了挺进报。
代码实现
public static int parseInt(String s) { String str = s; if (str == null || str == "") throw new NumberFormatException("For input string:\"" + s + "\""); //是否为负数 boolean negative = false; // 是否以+号或-号开头 if(str.startsWith("+") || str.startsWith("-")) { if (str.startsWith("-")) negative = true; str = str.substring(1); // 以+或-开头,但是后面没有数字 if (str == "" || str.length() == 0) throw new NumberFormatException("For input string:\"" + s + "\""); } char[] chars = str.toCharArray(); long result = 0; for (int i = 0; i < chars.length; i++) { // 是否是'0'到'9'之间的字符 if (chars[i] < '0' || chars[i] > '9') throw new NumberFormatException("For input string:\"" + s + "\""); // 先根据字符之间进行运算来得到int值,再根据每个数字所在的位数来计算应该乘10的几次幂(Math.pow()函数用于求幂),最后累加。 result += (chars[i] - '0') * Math.pow(10, chars.length - i - 1); // 是否超出int的最小值 if (negative && -result < Integer.MIN_VALUE) {throw new NumberFormatException("For input string:\"" + s + "\""); } // 是否超出int的最大值 if (夜叉鸦 !negative && result > Integer.MAX_VALUE) {throw new NumberFormatException("For input string:\"" + s + "\""); } } if (negative) result = -result; return (int) result;}
补充:上面的代码中,我将result变量定义成long类型,是因为输入的字符串可能是一个超出int允许范围的值,在执行累加运算部分代码的时候会因为溢出而导致计算结果不准确。我们可以尝试将上面代码中的变量result定义成int类型唐县全胜峡 ,然后输入一个超出int边界的值进行测试,运行上面的代码将不会抛出异常,而是输出一个int的边界值卡欧斯赛文,溢出的部分将被忽略,这显然是不准确的。所以虽然将变量result定义成了long类型,但是最终计算的结果会转成int类型返回。
上面提供的只是一个思路,比较容易理解,但就效率而言,可能并非最优解,大家有兴趣的话,可以研究下JDK官方的字符串转整数的算法,即Integer.valueOf()方法的源码,这里也大概说下它的实现思路,它的核心算法其实只有下面两行代码:
result *= 10;
变量result用于记录最终计算后的整数黄飞珏,初始值为0。
result -= digit;
变量digit表示在循环遍历得到的当前字符所对应的数字,由于result的初始值是0,通过上面的减法运算后得到的result肯定是一个负数。
上面这两行代码是什么意思呢?这里依然以"3256"这个字符串为例,应用到上面的两行代码,就是下面这个计算过程:
-3256 = ((-3*10-2)*10-5)*10-6
也就是:
-3256 = (-32*10-5)*10-6
也就是:
-3256 = -325*10-6
也就是:
-3256 = -3256
这个过程由于使用的是负数,可能不太好理解虫族雄王,我们反过来改成正数就是下面的样子:
3256 = ((3*10+2)*10+5)*10+6
也就是:
3256 = (32*10+5)*10+6
也就是:
3256 = 325*10+6
也就是:
3256 = 3256
总结成一句话就是:
将字符串3256从左向右进行遍历, 让每一位都先乘以10, 然后再加上它后面的那一位数, 依次类推,进行累加求和。
这个思路设计巧妙,使得每次迭代计算之后得出的值都不会很大,有利于提高计算效率。这就是Integer.valueOf()方法的核心思路,关于更详细的细节,感兴趣的话可以自己去研究一下果多美。
关于字符串转整数的算法,肯定不止以上两种思路,如果你是求职者,对于面试官提出的这个问题,你会怎么实现呢?
如果你喜欢我们的文章泪洒天堂 ,欢迎扫描下方二维码关注我们,或搜索公众号"学不死的程序员"进行关注,我们将持续为广大程序员们提供免费、优质和实用的视频课程: