JDK源码之Integer类—formatUnsignedInt()方法
Posted 二木成林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码之Integer类—formatUnsignedInt()方法相关的知识,希望对你有一定的参考价值。
前言
将十进制整数转换成其他进制的方法是:整数转换为r进制数时,采用除r取余方法,即将十进制整数不断除以r取余数,直到商为0,所得的余数按逆序排列。
图解:
用Java简单来实现这个代码:
public class Test {
public static void main(String[] args) {
System.out.println(Integer.toString(123456, 16));// 1e240
System.out.println(toString(123456, 16));// 1e240
System.out.println(Integer.toString(987654, 23));// 3c40b
System.out.println(toString(987654, 23));// 3c40b
}
public static String toString(int i, int radix) {
char[] digits = {
'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'
};
StringBuilder sb = new StringBuilder();
while (i > 0) {
sb.append(digits[i % radix]);
i /= radix;
}
return sb.reverse().toString();
}
}
formatUnsignedInt()方法
formatUnsignedInt()方法将整数val转换成指定进制数后,填充到字符数组buf中。
该方法的源码如下:
/**
* Format a long (treated as unsigned) into a character buffer.
* @param val the unsigned int to format
* @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary)
* @param buf the character buffer to write to
* @param offset the offset in the destination buffer to start at
* @param len the number of characters to write
* @return the lowest character location used
*/
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
int radix = 1 << shift;
int mask = radix - 1;
do {
buf[offset + --charPos] = Integer.digits[val & mask];
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
该方法被toUnsignedString0()方法调用,而toUnsignedString0()方法又被toBinaryString()方法、toOctalString()方法、toHexString()方法调用。明白这点是很重要的,因为要把它们关联起来阅读。
关于toUnsignedString0()方法请参考:JDK源码之Integer类—toUnsignedString0()方法
formatUnsignedInt()方法的注释如下:
/**
* 将long(视为无符号)格式化为字符串缓冲区
*
* @param val 要格式的无符号整数,int类型
* @param shift 要格式化的基数的log2(十六进制为4,八进制为3,二进制为1)
* @param buf 要写入的字符缓冲区,是一个字符数组
* @param offset 目标缓冲区中开始的偏移量
* @param len 要写入的字符数
* @return 返回使用的最低字符索引位置
*/
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
// 局部变量,将传入的len作为起始索引位置,倒序向字符数组中插入字符(即从数组的最后一个位置开始向前插入字符)
int charPos = len;
/*
这两行的作用是要得到对应进制数的掩码。
如果是二进制,因为2的1次方是2,所以shift就是1,那么radix就是1左移1位得到2,mask就是1,对应的二进制就是0001;
如果是八进制,因为2的3次方是8,所以shift就是3,那么radix就是1左移3位得到8,mask就是7,对应的二进制就是0111;
如果是十六进制,因为2的4次方是16,所以shift就是4,那么radix就是1左移4位得到16,mask就是15,对应的二进制就是1111;
*/
int radix = 1 << shift;
int mask = radix - 1;
/*
之前我们求十进制转换成其他进制就是不断除以进制数,最后组合得到的余数
但如果是转换成八进制或十六进制这种进制数为2的整数次方的进制,有更快的算法,就是先得到二进制,然后再每几位一组转换
比如将十进制数123转换成八进制:
第一步,先把123转换成二进制是:0111 1011
第二步,因为八进制的8是2的3次方,所以将0111 1011每三位一组划分,高位补0,即001 111 011
第三步,把001 111 011按每一组分别转换成八进制,也就是1 7 3,所以123的八进制表示是173
*/
do {
/*
循环val & mask的过程就像分组转换的过程,每次循环可以将mask对应的位数得到,再利用digits数组转换成对应的字符,
这个字符就是这次分组的这几位所对应的结果,每次分组得到结果以后把val右移相应的位数,继续下一轮的循环分组。
*/
/*
假如val=123,对应二进制是0111 1011,要求转换成八进制,那么mask等于7,锁对应的二进制是111,将val每三位分组后的二进制是001 111 011
第一次循环:val&mask保留低三位011,而val>>>=shift就是无符号右移三位,即覆盖原来的低三位,也为下一次循环保留低三位作准备
val&mask
001 111 011
&
000 000 111
=000 000 011(保留了val的低三位,011所表示的八进制是3,在digits数组中对应的字符是'3')
val>>>=shift
001 111 011
>>>
3
=000 001 111
第二次循环:val&mask保留低三位111,而val>>>=shift就是无符号右移三位,即覆盖原来的低三位,也为下一次循环保留低三位作准备
val&mask
000 001 111
&
000 000 111
=000 000 111(保留了val的低三位,111所表示的八进制是7,在digits数组中对应的字符是'7')
val>>>=shift
000 001 111
>>>
3
=000 000 001
第三次循环:val&mask保留低三位001,而val>>>=shift就是无符号右移三位,即覆盖原来的低三位,也为下一次循环保留低三位作准备
val&mask
000 000 001
&
000 000 111
=000 000 001(保留了val的低三位,001所表示的八进制是1,在digits数组中对应的字符是'1')
val>>>=shift
000 000 001
>>>
3
=000 000 000(val为0时结束循环)
*/
buf[offset + --charPos] = Integer.digits[val & mask];
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
该方法的核心就是将十进制转换成二进制或八进制或十六进制,不是使用的除数取余法,而是采用更加快捷的位运算。
关于本方法如何将十进制转换成二进制或八进制或十六进制,注释中虽然有示例说明,但下面给出图解。
关于具体将二进制转换成八进制和十六进制就不多言了。
以上是关于JDK源码之Integer类—formatUnsignedInt()方法的主要内容,如果未能解决你的问题,请参考以下文章
JDK源码之Integer类——rotateRight()方法
JDK源码之Integer类——rotateLeft()方法
JDK源码之Integer类——numberOfLeadingZeros()方法