想起前段时间在做算法题的时候,评论区里看到一条有趣的评论,原文是这样的:“别看现在讨论区里各种秀优化,等到真正面试的时候,面试官让你手写个进制转换你都得慌张”
哈哈,确实,平时做算法,考虑各种方法,想办法提高效率,降低空间的消耗,但实际真正面试的时候,可能情急之下还是只会拿出自己最熟悉的做法。
看到进制转换,我不禁也惊了一下,要我短时间内写出非常完美的代码,肯定也是为难的,而且一不留神,就会降低效率,增加空间的消耗,所以,我发现,这确实是个值得好好探究的问题。
谈到进制转换,第一印象是什么,反正我当时脑子里第一想的是栈,而且也只是一个模模糊糊的思路,感觉很急的情况下,难免会出现一些错误。
先不慌,我们看下JDK的大神们,是怎样写的。
假设进制转换的原理大家都非常清楚了哦 ~ 不清楚的百度一下啦 ~
JDK11-Integer-toString方法
Integer类中有一个静态方法:toString,其主要的作用就是进制转换,看看它到底有何神奇之处~~~
- 注释为个人所写。
/**注释:ATFWUS
* 说明:返回用第二个参数指定基数表示的第一个参数的字符串表示形式。
* @param i 待转换的十进制数
* @param radix 基数
* @return String
*/
public static String toString(int i, int radix) {
//如果基数小于 `Character.MIN_RADIX`(2) 或者大于 `Character.MAX_RADIX`(36),则改用基数 10。
**if (radix < 2 || radix > 36) {
radix = 10;
}
//基数等于10直接调用toString方法
if (radix == 10) {
return toString(i);
} else if (!String.COMPACT_STRINGS) {
return toStringUTF16(i, radix);//采用UTF16编码的转换
} else {
//buf数组存储转换后的结果
byte[] buf = new byte[33];
boolean negative = i < 0;
int charPos = 32;
//如果i大于0,也先转换成负数,便于后续的转换
if (!negative) {
i = -i;
}
//进制转换核心代码,对进制取余后存入buf数组
while(i <= -radix) {
buf[charPos--] = (byte)digits[-(i % radix)];
i /= radix;
}
//最后还要进行一次转换
buf[charPos] = (byte)digits[-i];
//如果i小于0,在结果的最前面加上负号
if (negative) {**
--charPos;
buf[charPos] = 45;
}
//返回buf数组的有效位数的字符串
return StringLatin1.newString(buf, charPos, 33 - charPos);
}
}
- 其中,上面的
digits
是Integer类中的常值数组。
static final char[] digits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
官方API说明:
-
public static String toString(int i, int radix)
-
参数:
- i - 要转换成字符串的整数。
- radix - 用于字符串表示形式的基数。
-
返回值:
- String类型,使用指定基数的参数的字符串表示形式。
-
返回用第二个参数指定基数表示的第一个参数的字符串表示形式。
-
如果基数小于
Character.MIN_RADIX
或者大于Character.MAX_RADIX
,则改用基数 10。 -
如果第一个参数为负,则结果中的第一个元素为 ASCII 的减号
'-'
('\u002D'
)。如果第一个参数为非负,则没有符号字符出现在结果中。
逐行阅读,欣赏JDK作者的编码风格!
-
第一个判断,判断基数的有效性,如果超过指定的基数范围,则返回原数字。
- 【数据有效性的判断】
-
然后,如果基数为10,直接调用将该数字转换为字符串的方法。
- 【特殊情况的考虑】
-
紧接着,就是正文了,若是其它基数,就开始进行转换。
-
定义一个长度为33的
buf
字节数组。-
为什么是字节数组?
- 因为字节数组的大小是-128到+127,处理这些单个的数字足矣。
- 【合适数据类型的选取】
-
为什么长度是33?
- 因为一个int类型的数据,转换成二进制最多也才32位,再加上考虑符号的问题,设成33已经足够满足所有有效的转换了。
- 【对数组大小的合理估计】
-
-
定义了一个
boolean
类型的negtive
,用于判断i
是否小于0。- 【符号处理一】
-
定义了一个
charPos
,用于控制数组下标。(其实用byte也可以) -
如果
i
是正数,先将正数变成负数,方便后续统一的进行进制转换。- 【符号处理二】
-
接下来就是进制转换的核心部分了:
-
循环条件是:
i<=-radix
,这里的i
始终是负数,radix
始终是正数 -
buf[charPos--] = (byte)digits[-(i % radix)];
- 【巧妙的利用一个静态数组
digits
,使得任何进制的转换统一化】。 - 【倒序存储,模仿进制转换栈原理】。
- 【巧妙的利用一个静态数组
-
i /= radix;
,进制转换的原理。
-
-
退出循环后,
i
的最后部分也需要加入byte数组。 -
如果
i
小于0,在最前面加上负号。(负号的ASCII码为45)- 【符号处理三】
-
最后将buf数组有效转换成字符串输出。
代码看完了,怎么样?是不是发现JDK作者的这种编码风格非常的好呀,非常严谨,对细节处理的非常到位,难怪说JDK源码是java程序员的字典,确实,里面有很多东西值得我们去思考!
- 以后面试官再让你手写进制转换,直接默写源码!