专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Integer和Long部分源码分析

Integer和Long的java中使用特别广泛,本人主要一下Integer.toString(int i)和Long.toString(long i)方法,其他方法都比较容易理解。

Integer.toString(int i)和Long.toString(long i),以Integer.toString(int i)为例,先看源码:

    /**
      * Returns a {@code String} object representing the
      * specified integer. The argument is converted to signed decimal
      * representation and returned as a string, exactly as if the
      * argument and radix 10 were given as arguments to the {@link
      * #toString(int, int)} method.
      *
      * @param   i   an integer to be converted.
      * @return  a string representation of the argument in base 10.
      */
     public static String toString(int i) {
         if (i == Integer.MIN_VALUE)
             return "-2147483648";
         int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
         char[] buf = new char[size];
         getChars(i, size, buf);
         return new String(buf, true);
     }

通过调用stringSize来计算i的长度,也就是位数,用来分配合适大小的字符数组buf,然后调用getChars来设置buf的值。

stringSize的Integer和Long中的实现有所不同,先看看源码

Integer.stringSize(int x)源码:

    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;
     }

将数据存放在数组中,数组中的下标+1就是i的长度,当x小于sizeTable中的某个值时,这样设计只需要循环就可以得出长度,效率高。

Long.stringSize(long x)源码:

     // Requires positive x
     static int stringSize(long x) {
         long p = 10;
         for (int i=1; i<19; i++) {
             if (x < p)
                 return i;
             p = 10*p;
         }
         return 19;
     }

因为Long的十进制最大长度是19,在计算长度时通过反复乘以10的方式求出来的,可能会问为什么不用Integer.stringSize(int x)的方法,我也没有找到合适的解释。

传统的方案可能是通过反复除以10的方法求出来的,但是这样的效率低,因为计算机在处理乘法时要比除法快。

getChars(int i, int index, char[] buf)源码:

    /**
      * Places characters representing the integer i into the
      * character array buf. The characters are placed into
      * the buffer backwards starting with the least significant
      * digit at the specified index (exclusive), and working
      * backwards from there.
      *
      * Will fail if i == Integer.MIN_VALUE
      */
     static void getChars(int i, int index, char[] buf) {
         int q, r;
         int charPos = index;
         char sign = 0;

         if (i < 0) {
             sign = '-';
             i = -i;
         }

         // 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];
         }

         // 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;
         }
         if (sign != 0) {
             buf [--charPos] = sign;
         }
     }

这是整个转换过程的核心代码,首先确定符号,其次当i>=65536时将i除以100,并且通过DigitOnes[r]和DigitTens[r]来获取十位和个位上的值,因为除法慢,所以一次性除以100提高效率,DigitOnes和DigitTens如下:

   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 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',
         } ;

假设r=34,通过查表可以得出DigitOnes[r]=4,DigitTens[r]=3。

1 q = (i * 52429) >>> (16+3); 的本质是将i/10,并去掉小数部分,219=524288,52429/524288=0.10000038146972656,为什么会选择52429/524288呢,看了下面就知道了:

 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

可以看出52429/524288的精度最高,并且在Integer的取值范围内。

1 r = i – ((q << 3) + (q << 1)); // r = i-(q*10) … 用位运算而不用乘法也是为了提高效率。

注:以上分析内容仅个人观点(部分参考网上),如有不正确的地方希望可以相互交流。

文章永久链接:https://tech.souyunku.com/23762

未经允许不得转载:搜云库技术团队 » Integer和Long部分源码分析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们