源码
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
// BEGIN Android-changed: Cache the String for small values.
// int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
// 注意这里的BEGIN Android-changed和 END Android-changed 注释
// 在AndroidStudio中读源码有这一段
// 在Intellij中读到的源码是没有BEGIN和END之间的这一段代码的,
// 个人理解,不太恰当的比喻下就是android的jre和传统Java开发的jre不一样。
// 这个方法在不同运行环境下的真正实现不一样
boolean negative = i < 0;
boolean small = negative ? i > -100 : i < 100;
// 判断值在区间(-100,100),采用类似缓存机制的方式来读取
if (small) {
// SMALL_NEG_VALUES 和 SMALL_NONNEG_VALUES 分别是(-100,0)和 [0,100)两个区间正数对应字符串的缓存池
// 以私有静态常量数组的形式被声明在Integer中,随着Integer的初始化而初始化,初始情况下为空,长度为100
final String[] smallValues = negative ? SMALL_NEG_VALUES : SMALL_NONNEG_VALUES;
if (negative) {
// 在值为负数的情况下,将其视作正数来进行处理
i = -i;
// 在smallValues数组中直接根据 i 作为索引查询字符串
if (smallValues[i] == null) {
// DigitOnes和DigitTens 是两个静态常量数组,值已经写入好了,长度为100
// 内容是位于区间 [0,99]的100个数,按照从0到99的顺序,分别对应的个位数的字符和十位数的字符
smallValues[i] =
// 如果小于10,直接赋值smallValues[i] ‘-’+(查询得到的个位数对应字符)
i < 10 ? new String(new char[]{'-', DigitOnes[i]})
// 否则赋值smallValues[i] ‘-’+(十位数字符+个位数字符)
: new String(new char[]{'-', DigitTens[i], DigitOnes[i]});
// 如果仅采用三元表达式 末尾 十+个 的方式,会给小于10的数字符补上0,所以不对
}
} else {
// 大概原理同上
if (smallValues[i] == null) {
smallValues[i] =
i < 10 ? new String(new char[]{DigitOnes[i]})
: new String(new char[]{DigitTens[i], DigitOnes[i]});
}
}
// 位于(-100,100)区间的数,经过上述代码块,已经在smallValue对应的缓存数组中生成或查询到了值
// 直接按照索引返回即可
return smallValues[i];
}
// 对于位于(-∞,-100]和区间[100,∞)的数,先计算位数
int size = negative ? stringSize(-i) + 1 : stringSize(i);
// stringSize(i)方法用于计算数字的位数
//如果是负数还要留出一位符号位
// END Android-changed: Cache the String for small values.
char[] buf = new char[size]; // 生成对应位数的char数组
getChars(i, size, buf); // getChars(...)方法将每一位对应的char放入数组
// Android-changed: Use regular constructor instead of one which takes over "buf".
// return new String(buf, true);
return new String(buf); // 返回char数组组成的字符串
}
getChars(int i, int index, char[] buf)方法
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
// 负数全部当作正数处理
// 并单独记录符号为sign
sign = '-';
i = -i;
}
// Generate two digits per iteration
// 当值大于65536时,每次循环,生成2位字符
// iEg=1165536,buf=[ , , , , , , ] charPos=7
while (i >= 65536) {
q = i / 100; // q记录了下次循环时i 对应的初始数
// qEg=11655,则本次应该写入‘3’,‘6’
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
// (q<<6)+(q<<5)+(q<<2)==q*2^6+q*2^5+q*2^2==q*(64+32+4)==q*100
// 采用左移的方式相比直接*100,在纳秒级别可以看出,左移会明显快很多。毫秒级别可能看不太清楚。
// r= i-(q*100) ==i-((i/100)*100)==i Mod(100) 既是该次应该写入的两位字符对应的值
// rEg=36
i = q;
buf [--charPos] = DigitOnes[r]; // 利用之前的DigitOnes和DigitTens
buf [--charPos] = DigitTens[r];
// bufEg=[ , , , , ,'3','6'] charPosEg=5,iEg=11655,下一轮会跳出循环
}
// iEg=11655
// 前面将 从第0位到第3位的数记录到了buf中
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (i * 52429) >>> (16+3);
// 2^19=524288
// q=( i* ((2^19+2)/10)) / 2^19 == i/10 ((2^19+2)/2^19 是int除int 在 jvm中计算结果为1)
// 但是为什么要写成 16+3?表示+3是精心选择过的意义吗?意义又在哪?
// 前面为什么又要选65536=2^16,选这几个数的道理是什么?没搞懂。
// qEg=1165
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
// 同上 r=i-q*(2^3+2)
// rEg=11655-1165*10=5
buf [--charPos] = digits [r];
// digits[]是一个36位char数组,内容为‘0’~‘9’,‘a’~'z'
// 在大于10进制的进制的toString中还会用到,所以有后面的'a'~'z'
// 此时buf=[ , , , ,5,3,6],charPosEg=4
i = q;
// iEg=1165,之后循环直到 i==0
if (i == 0) break;
}
if (sign != 0) {
// 如果存在符号'-',则在第一位放入符号
buf [--charPos] = sign;
}
}
stringSize方法,原理一看便知,不再赘述。
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
// Requires positive x
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}