总览
微信截图_20180418144219.pngInteger是int类型的包装类,继承自Number抽象类,实现了Comparable接口。提供了一些处理int类型的方法,比如int到String类型的转换方法或String类型到int类型的转换方法,当然也包含与其他类型之间的转换方法。
- Comparable提供了比较大小的功能
-
Number抽象类主要抽象出了对数值类型的转换方法。可以看下图:
微信截图_20180418150806.png
属性
private final int value;
@Native public static final int MIN_VALUE = 0x80000000;
@Native public static final int MAX_VALUE = 0x7fffffff;
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
@Native public static final int SIZE = 32;
public static final int BYTES = SIZE / Byte.SIZE;
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'
};
final static char [] DigitOnes = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
} ;
final static char [] DigitTens = {
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
} ;
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
- value:表示Integer对应的int值。
- MIN_VALUE:定义一个常量,表示int类型的最小值,-231,@Native 表示被注解的内容是原生,不影响java代码的本身逻辑。
- MAX_VALUE:定义一个常量,表示int类型的最大值,231-1。
- TYPE:表示这个包装类包装的是基本类型int。
- Size:定义常量,用于以二进制补码形式表示int值的位数。
- BYTES:定义常量,用于以二进制补码形式表示int值的字节数。
- digits:表示所有可能用来小表示数字的字符,因为int是支持2-36进制,所以需要36个字符在表示不同的数字。
- DigitOnes和DigitTens:使用DigitOnes和DigitTens来确定一个两位数int对应的char。如65,DigitOnes[65]=5,DigitTens[65]=6。
- sizeTable:主要用来计算一个int类型对应字符串的长度。如下的stringSize方法
方法分析
toString
将int转成字符串
public static String toString(int i) {
// 先判断i是不是最小值,如果是就直接返回
if (i == Integer.MIN_VALUE)
return "-2147483648";
// 调用stringSize计算int值对应字符串的长度,负数有一个符号位所以要加一
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
// 新建一个临时数组,存放int值的每一位转成char后的值
char[] buf = new char[size];
// 将int值的每一位转成char后放到buf中
getChars(i, size, buf);
// 返回一个字符串
return new String(buf, true);
}
stringSize
获取一个int值对应字符串的长度。
// 利用sizeTable属性,通过该方法可以高效的去获取一个int值对应字符串的长度,而不必进行除法或者取模运算
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
getChars
在toString方法中调用,主要所用是,将int值的每一位转成char后放到buf中。
static void getChars(int i, int index, char[] buf) {
int q, r;
// buf数组的长度
int charPos = index;
// 符号标示位
char sign = 0;
// 如果i是负数,那么将符号位设为“-”,并且取i的相反数。
if (i < 0) {
sign = '-';
i = -i;
}
// Generate two digits per iteration
//③如果i大于63336,两个字节的长度,那么久每次取1的后两位出来处理
while (i >= 65536) {
// 去掉i的后两位后并将值赋值给q,如果i=65537,那么现在q=655
q = i / 100;
// really: r = i - (q * 100);
// ①计算后两位数字,如果i=65537,那么r=37,计算公式就是【 r = i - (q * 100)】
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
// 通过DigitOnes和DigitTens获取r的个位和十位对应的char。
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
// ②就是q = i/10,如果i=655,那么q=65
q = (i * 52429) >>> (16+3);
// 取i的最后一位,就是r=i-(q*10),r=5
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
// 通过digits数组获取对应的char
buf [--charPos] = digits [r];
// q=65,并赋值给i,进入下一个循环
i = q;
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign;
}
}
说明:
- ① (q << 6) + (q << 5) + (q << 2)=q * 100。可以这样子来推导,(q << 6) + (q << 5) + (q << 2) = q * 2^6 + q * 2^5 + q * 2^2 = q * (2^6 + 2^5 + 2^2) = q * (64 + 32 + 4) = q * 100。在这里将算数运算全部优化成了位运算,位运算也是计算机里面最快的运算,q=i/100也刚好和这里为位运算相吻合。从这里就可以看出JDK开发对效率已经优化到了极致。
- ② (i * 52429) >>> (16+3) = i/10。能增加运算速度,主要是因为乘法运算要比除法运算快而且,除法可能产生浮点数。先来看一下推导公式,可以看到下面的备注52429>>>19=52429/(1<<19)=52429/2^19=52429/524288=0.10000038146972656,如果i在5位数范围内,那么(i * 52429) >>> (16+3) = i/10。
2^10=1024, 103/1024=0.1005859375
2^11=2048, 205/2048=0.10009765625
2^12=4096, 410/4096=0.10009765625
2^13=8192, 820/8192=0.10009765625
2^14=16384, 1639/16384=0.10003662109375
2^15=32768, 3277/32768=0.100006103515625
2^16=65536, 6554/65536=0.100006103515625
2^17=131072, 13108/131072=0.100006103515625
2^18=262144, 26215/262144=0.10000228881835938
2^19=524288, 52429/524288=0.10000038146972656
选19是在不超出整形范围内,精度最高的。
- ③ 这里的i为啥小于65536呢,这是是为了保证(i * 52429)不会溢出。
不知道有没有人发现,其实65536* 52429是超过了int的最大值的,一旦超过就要溢出,那么为什么还能保证(i * 52429)>>> 19能得到正确的结果呢? 这和>>>有关,因为>>>表示无符号右移,他会在忽略符号位,空位都以0补齐。一个有符号的整数能表示的范围是 -2147483648至2147483647,但是无符号的整数能表示的范围就是 0-4,294,967,296(2^32),所以,只要保证i * 52429的值不超过2^32 次方就可以了。65536是2^16 ,52429正好小于2^16,所以,他们的乘积在无符号向右移位就能保证数字的准确性。
网友评论