成员变量
@Native public static final int MIN_VALUE = 0x80000000;
@Native public static final int MAX_VALUE = 0x7fffffff;
复制代码
MIN_VALUE表示是Integer的最小值,也就是-2^31次方。MAX_VALUE表示Integer的最大值,也就是2^31-1。源码中使用补码的形式来表示Integer的值,由于Integer的最大值是2^31-1, 当我们对它进行加1的时候,也就是变成2^31,显然这个就是溢出了,会导致回拨到-2^31次方,也就是Integer的最小值了。因此两个成员变量的关系是:
0x80000000 = 0x7fffffff + 1。
toString源码解析
toString(int i, int radix)
toString在进行转换为进制字符串时,是统一以负数进行转换的,最后再针对是否负数来决定是否添加“-”号。
那么,还有一个问题,就是整型是如何转换为char类型的?
源码中先预置了一个char数组,如下:
final static 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'
};
复制代码
既然,内置了数组,那么只有通过数组的下标就能够取出对应的字符, 将这些字符统一放到char[]中,那么就可以构造出字符串了。Integer中计算下标的算法是:
while (i <= -radix) {
buf[charPos--] = digits[-(i % radix)];
i = i / radix;
}
buf[charPos] = digits[-i];
复制代码
这一块参考【取商留余】进制转换的算法,就很容易理解了。
toString(int i)
从图上可以看出,10进制int转换字符串的流程还是比较简单的,其实,其核心的流程都是再getChar()方法里面了。这里主要对图上绿色部分进行说明,也即是计算char数组的大小。
10进制转换为字符串,首先要创建char[]数组来记录转换的字符,那么Integer通过以下的方法来计算出char数组的大小:
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
// Requires positive x
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
复制代码
调用stringSize方法前,需要确定入参x是正数,因此以上会先对i是否负数进行判断,若是负数,那么就需要对i转换为负数, 如stringSize(-i),但由于在有符号整型的范围内,负数是比正数多一位的,因此这里需要+1。
那么,stringSize是如何根据x来得出它的位数的呢?我们都知道数字9是个位与十位的临界值,因此Integer也使用了以下数组来判断x的位数,如下:
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
复制代码
我们假设x的值是10000,那么stirngSize通过遍历,可以比较出9999是小于等于10000的,那么9999在sizeTable数组中的下标是4,正好4也是9999的位数,这里很巧妙。最后,加1得出的就是x的位数了。
getChar源码解析
getChar方法的核心流程主要就是迭代生成字符的过程(对应图上绿色的节点),以下分别进行说明。
每次迭代生成两位字符处理源码,如下:
// Generate two digits per iteration
while (i >= 65536) {
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
复制代码
这段代码的处理逻辑如下:
当 i >= 65536时,是每两位取出数字,i /= 100,例如 i = 567235474。
- 先取最后两位 7 和 4 放入buf数组中,i = 5672354,buf = { , , , , , , , ‘7’, ‘4’};
- 再取最后两位 5 和 4 放入buf数组中,i = 56723,buf = { , , , , , ‘5’, ‘4’, ‘7’, ‘4’};
- i此时已经小于65536了,跳出循环。
那么以上的r = i – ((q << 6) + (q << 5) + (q << 2))的作用相当于r = i – (q * 100), 因为位移的性能是比乘法要高的,因此采用了转换处理。具体推导过程如下:
- r = i – (q * 100)
- r = i – (q * 64 + q * 32 + q * 4)
- r = i – ((q << 6) + (q << 5) + (q << 2))
跳出循环后,进入到每次迭代生成1位字符的处理中,代码如下:
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
复制代码
当 i < 65536时,时每一位取出数字,i /= 10, 接着以上的例子, i = 56723。
- 先取最后一位 3 放入到buf数组中,i = 5672, buf = { , , , , ‘3’ , ‘5’, ‘4’, ‘7’, ‘4’}; 循环执行这一步,直到i == 0。
以上q = (i * 52429) >>> (16+3)的作用相当于q = i/10。r = i – ((q << 3) + (q << 1)) 的作用相当于r = i-(q*10)。
toUnsignedLong源码解析
toUnsignedLong主要功能是将int值转换为无符号的long值,源码如下:
public static long toUnsignedLong(int x) {
return ((long) x) & 0xffffffffL;
}
复制代码
整体代码的意思是:将int转换为unsinged Long。需要考虑正负数两种情况,正数的话,是保持一致的。如果是负数,那么负值等于加2^32。
我们针对负数的情况,进行一下验证,流程如下:
负数的运算过程:
oxfffffff补码 : 1000 0000 0000 0000 0000 0000 0000 0001
-2补码: 1111 1111 1111 1111 1111 1111 1111 1110
&运算 1000 0000 0000 0000 0000 0000 0000 0000
转换为原码: 1111 1111 1111 1111 1111 1111 1111 1110
10进制: 4294967294 = 2^32 - 2
复制代码