在某一次面试中,面试官问了我一个问题,他说,String str = “1234”;如何在不使用 JDK自带方法的情况下,转换成 int i = “123”。当时想法思路都有了,但是没写出来......
本篇将讲述一下 String 与 int 两者之间是如何相互转换的,推荐用时:2小时。
String 转 int
其实这种东西JDK早就给你想到了,咱们看一下int的封装数据类型是怎么解决这个问题的;
Integer.parseInt(s)
private static void stringToIntOne() {
String s = "1234";
int i = Integer.parseInt(s);
System.out.println(i);
}
输出结果:
源码分析
将字符串参数解析为带符号的十进制整数,前提是字符串的内容为整数,且 可以存在 +号或者-号。
ASCII {@code '-'} ({@code '\u005Cu002D'})
ASCII {@code '+'} ({@code '\u005Cu002B'})
Integer.parseInt();方法底层其实调用的是
parseInt(String s, int radix)
throws NumberFormatException{}
如果出现以下任何一种情况,则抛出类型{@code NumberFormatException}的异常:
- 第一个参数,是{@code null}或者是这个字符串长度为零‘
- 第二个参数,也就是进制基数小于{@link java.lang.Character#MIN_RADIX}或大于{@link java.lang.Character#MAX_RADIX},最小值为2,最大值为36;
- 字符串的任何字符都不是指定的数字,但第一个字符可能是减号{@code' - '}({@code'\ u005Cu002D'})或加号{@code'+'}({@code'\ u005Cu002B'})前提是字符串长度超过1;
- 字符串内容不是int类型的内容;
看一下源码主干
1.8的和1.7的源代码一样,这里就不冗余贴出来了,对应jdk1.8源代码并做了相应的注解源码如下所示,帮助大家一起更加深入的理解:
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.
*/
// 下面三个判断好理解,其中表示进制的 radix 要在(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");
}
/**
* 表示结果, 在下面的计算中会一直是个负数
* 假如说 我们的字符串是一个正数 "7",那么在返回这个值之前result保存的是 -7
* 这个可能是为了保持正数和负数在下面计算的一致性
*/
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
/**
* limit 默认初始化为 最大正整数的 负数 ,假如字符串表示的是正数
* 那么result(在返回之前一直是负数形式)就必须和这个最大正数的负数来比较,判断是否溢出
*/
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;
/**
* len为输入字符串的长度,i为循环len自增变量
* result初始值为0,multmin初始值为最大负整数/进制数
*/
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
//根据Character类获取当前对应字符对应进制的数字
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
/**
* 这里就是上面说的判断溢出,由于result统一用负值来计算,所以用了 小于 号
*/
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
/**
* 再来个假设:一开始输入一个数字字符串为123,那么对应的radix=10(因为是10进制的),digit = 123 / 10 计算得到的
* 第一次result *= radix --> result = 0 ; result -= digit --> result = -1
* 第二次result *= radix --> result = -10; result -= digit --> result = -12
* 第三次result *= radix --> result = -12; result -= digit --> result = -123
* 此时,negative = false,则返回 -result,即最终结果为:123
*/
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
分析步骤:
前置条件:定义int返回值,是否为负数bool,循环遍历初始值,字符串长度值,int的最小值(-2^31-1)
- 判断是否为null,判断是否低于2进制,大于36进制,判断长度是否大于0,满足以上判断都抛异常;
- 将String 转换成 char数组,取第一个字符与字符串“0”比较,判断是否是“+”,“-”;
- 如果是负数,将定义负数的bool给于true操作,将初始值给于int最小值;
- 判断长度为1且不是+-,抛出异常;
- 为防止溢出,定义一个最小值除以进制的数;
- while 循环,分别通过Character方法,取出对应的字符值;
- 比较返回的结果是否小于定义防止溢出的值,统一用负数来比较,所以溢出值默认用-;
- 返回结果在赋值成除进制之前的值,然后在减去上一次循环获取到的值;
比如:
- 开始输入一个数字字符串为123,那么对应的radix=10(因为是10进制的),digit = 123 / 10 计算得到的
- 第一次result *= radix --> result = 0 ; result -= digit --> result = -1
- 第二次result *= radix --> result = -10; result -= digit --> result = -12
- 第三次result *= radix --> result = -12; result -= digit --> result = -123
- 此时,negative = false,则返回 -result,即最终结果为:123
从以上源码可以看出,作者在计算整型数字的时候是采用取反的操作,即正数是通过负数进行计算,返回再取反操作。我猜想这样是为了在计算正负数的时候采取相同的策略,不需要为正数写一套方案,负数写一套方案,可以避免重复代码的嫌疑。
JDK中的举例
* <p>Examples:
* <blockquote><pre>
* parseInt("0", 10) returns 0
* parseInt("473", 10) returns 473
* parseInt("+42", 10) returns 42
* parseInt("-0", 10) returns 0
* parseInt("-FF", 16) returns -255
* parseInt("1100110", 2) returns 102
* parseInt("2147483647", 10) returns 2147483647
* parseInt("-2147483648", 10) returns -2147483648
* parseInt("2147483648", 10) throws a NumberFormatException
* parseInt("99", 8) throws a NumberFormatException
* parseInt("Kona", 10) throws a NumberFormatException
* parseInt("Kona", 27) returns 411787
* </pre></blockquote>
Integer.valueOf(s)
private static void stringToIntTwo() {
String s = "5678";
int i = Integer.valueOf(s);
System.out.println(i);
}
输出结果:
源码分析
同:Integer.parseInt(s),不做过多叙述。
关于 Integer.valueOf():
Integer.parseInt(s)与Integer.parseInt(s)的区别
Integer.parseInt(s)返回值为Int型,而Integer.valueOf(s)返回值为Integer,区别在于后者能够使用Integer的一些方法。
在不适用Integer自带方法情况下,自行实现
这个是面试题,划重点:
private static void stringToIntThree() {
String s = "18188";
// 把字符串转换成数组
String[] split = s.split("");
// 定义输出值 num 初始值为0
double num = 0;
// 获取数组 长度,用于计算
int arr = split.length;
for (int i = 0; i < split.length; i++) {
int c = Character.digit(s.charAt(i),10);
num += c*Math.pow(10,arr-i-1);
}
System.out.println((int)num);
}
输出结果
int 转 String
其实万事万物,皆可 toString();
Integer.toString(i)
private static void intToStringOne() {
int i = 10000;
String sss= Integer.toString(i);
System.out.println(sss);
}
输出结果:
其实万事万物,皆可 valueOf();
String.valueOf(i)
private static void intToStringTwo() {
int i = 10010;
String ss = String.valueOf(i);
System.out.println(ss);
}
输出结果:
源码分析
返回{@code int}参数的字符串表示形式。
toString():
最终是 new String();
“”+
private static void intToStringThree() {
int i = 8888;
String str=i+"";
System.out.println(str);
}
输出结果: