Java Integer源码阅读笔记

成员变量

    @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)

Integer toString进制转换流程图.png

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)

Integer toString十进制转换流程图 (2).png

从图上可以看出,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源码解析

Integer getChar流程图.png

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。

  1. 先取最后两位 7 和 4 放入buf数组中,i = 5672354,buf = { , , , , , , , ‘7’, ‘4’};
  2. 再取最后两位 5 和 4 放入buf数组中,i = 56723,buf = { , , , , , ‘5’, ‘4’, ‘7’, ‘4’};
  3. i此时已经小于65536了,跳出循环。

那么以上的r = i – ((q << 6) + (q << 5) + (q << 2))的作用相当于r = i – (q * 100), 因为位移的性能是比乘法要高的,因此采用了转换处理。具体推导过程如下:

  1. r = i – (q * 100)
  2. r = i – (q * 64 + q * 32 + q * 4)
  3. 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。

  1. 先取最后一位 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
复制代码

参考

探索Integer番外篇之Integer.getChars

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享