美文网首页
Integer源码

Integer源码

作者: xiaolyuh | 来源:发表于2019-07-17 16:13 被阅读0次

    总览

    微信截图_20180418144219.png

    Integer是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,所以,他们的乘积在无符号向右移位就能保证数字的准确性。

    相关文章

      网友评论

          本文标题:Integer源码

          本文链接:https://www.haomeiwen.com/subject/vuyvkftx.html