jdk源码整数和字符串间的转换

如果让您亲自动手来来写一个Integer中的toString()方法和parseInt()方法,您会怎么写?
请您先动手写toString()方法。

我对于toString()的思路:
1.首先需要得到这个Integer数值的位数,所以肯定有一个getIntegerSize()的private方法
2.然后要把这个Integer数值转化为一个字符数组,所以也要有一个getChars()的private方法
3.最后只要把这个字符数组转化为字符串返回即可

在编写完我们的代码,再确定对于现在自己的实力,已经没有再做优化的可能之后,再来看Integer中的toString()来领教一下高手的代码是怎么写的。

下面是这个过程中最重要的一步:将数值转成字符数组;
其中用使用来三个表(DigitOnes、DigitTens、digits)作为表驱动法(在代码大全上看到过,但还没用过:P)的快速索引来加快代码的执行效率。

Java代码 
  1. /** 
  2.  * @param i 被转十进制数 
  3.  * @param index 这个十进制数的总位数 
  4.  * @param buf 用来存储转化后的结果 
  5.  */  
  6. static void getChars(int i, int index, char[] buf) {  
  7.  int q, r;  
  8.  int charPos = index;  
  9.  char sign = 0;  
  10.   
  11.  if (i < 0) {  
  12.      sign = '-';  
  13.      i = -i;  
  14.  }  
  15.   
  16.  // Generate two digits per iteration  
  17.  while ( i >= 65536 ) {  
  18.      q = i / 100;  
  19.      r = i - ( ( q << 6 ) + ( q << 5 ) + ( q << 2 ) ); // 高效实现 r = i - ( q * 100 );  
  20.      i = q;  
  21.      buf [ --charPos ] = DigitOnes[ r ];  
  22.      buf [ --charPos ] = DigitTens[ r ];  
  23.  }  
  24.   
  25.  // Fall thru to fast mode for smaller numbers  
  26.  // assert( i <= 65536, i );  
  27.  for (;;) {  
  28.      q = ( i * 52429 ) >>> (16+3 ); //实现 q = i / 10(其中使用整数乘法后再移位来代替浮点数的乘法(q * 0.1),但对于现在的CPU来说不        //知是否还是整数乘法较快?)  
  29.      r = i - ( ( q << 3 ) + ( q << 1 ) );  //高效实现 r = i - ( q * 10 ) ...  
  30.      buf [ --charPos ] = digits [ r ];  
  31.      i = q;  
  32.      if ( i == 0 ) break;  
  33.  }  
  34.  if ( sign != 0 ) {  
  35.      buf [ --charPos ] = sign;  
  36.  }  
  37. }  

在给出DigitOnes、DigitTens、digits的三个表之后,我相信看懂这段代码应该是比较简单的事情了(可以写一个具体的数字,比如666666带进去试一下)。但是采用上面的代码来实现仍然存在一个缺陷,就是Integer.MIN_VALUE是不能采用这种方式来转换的。以为当其转化为整数后将要超出Integer的最大范围,所以在toString()方法中还要加入预防措施。

Java代码 
  1. /** 
  2.  * All possible chars for representing a number as a String 
  3.  */  
  4.  final static char[] digits = {  
  5.   '0' , '1' , '2' , '3' , '4' , '5' ,  
  6.   '6' , '7' , '8' , '9' , 'a' , 'b' ,  
  7.   'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,  
  8.   'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,  
  9.   'o' , 'p' , 'q' , 'r' , 's' , 't' ,  
  10.   'u' , 'v' , 'w' , 'x' , 'y' , 'z'  
  11.  };  
  12.    
  13.  /** 
  14.   * 传入一个二位十进制数,返回其十位上的数值 
  15.   */  
  16.  final static char [] DigitTens = {  
  17.   '0''0''0''0''0''0''0''0''0''0',  
  18.   '1''1''1''1''1''1''1''1''1''1',  
  19.   '2''2''2''2''2''2''2''2''2''2',  
  20.   '3''3''3''3''3''3''3''3''3''3',  
  21.   '4''4''4''4''4''4''4''4''4''4',  
  22.   '5''5''5''5''5''5''5''5''5''5',  
  23.   '6''6''6''6''6''6''6''6''6''6',  
  24.   '7''7''7''7''7''7''7''7''7''7',  
  25.   '8''8''8''8''8''8''8''8''8''8',  
  26.   '9''9''9''9''9''9''9''9''9''9',  
  27.  } ;  
  28.    
  29.  /** 
  30.   * 传入一个二位十进制数,返回其个位上的数值 
  31.   */  
  32.  final static char [] DigitOnes = {  
  33.   '0''1''2''3''4''5''6''7''8''9',  
  34.   '0''1''2''3''4''5''6''7''8''9',  
  35.   '0''1''2''3''4''5''6''7''8''9',  
  36.   '0''1''2''3''4''5''6''7''8''9',  
  37.   '0''1''2''3''4''5''6''7''8''9',  
  38.   '0''1''2''3''4''5''6''7''8''9',  
  39.   '0''1''2''3''4''5''6''7''8''9',  
  40.   '0''1''2''3''4''5''6''7''8''9',  
  41.   '0''1''2''3''4''5''6''7''8''9',  
  42.   '0''1''2''3''4''5''6''7''8''9',  
  43.  } ;  

虽然API的实现耗费了不少空间,但是其效率要比我写的要快多了(- -!!)。如果大家不明白上面的位移操作符可以看:Java中的位移运算符

对于得到一个整数数值的位数,我采用了的是不断取余操作(不知道多少兄弟和我想到一块儿去了,^ ^。但是取余操作效率相比之下要慢上不少)。那么API是如何快速得到一个整数数值的位数的呢?其中又采用了表驱动法来取代if-else:

Java代码 
  1. final static int [] sizeTable = { 9999999999999999999999999999,  
  2.                                       99999999999999999, Integer.MAX_VALUE };  
  3.   
  4.  // Requires positive x  
  5.  static int stringSize( int x ) {  
  6.   for ( int i=0; ; i++ )  
  7.       if ( x <= sizeTable[i] )  
  8.    return i+1;  
  9.  }  
  10.   
  11.  好了,现在是时候来看一下toString()方法了  
  12.  public static String toString( int i ) {  
  13.   if ( i == Integer.MIN_VALUE )  
  14.    return "-2147483648";  
  15.   int size = ( i < 0 ) ? stringSize( -i ) + 1 : stringSize( i );  
  16.   char[] buf = new char[ size ];  
  17.   getChars( i, size, buf );  
  18.   return new String( 0, size, buf );  
  19.  }  

这样toString()方法就实现了。

接下来请大家动手写parseInt()的方法:
下面是我的思路
1.首先自然也是要取得这个字符串的长度,不过这里很简单,直接使用String的length()方法就可以了
2.取出字符串中的第一个字符,判断是否是正负符号。如果不是的话则判断是否符合整数的条件,由于
这里取出的整数和ASCII码相差48,取出的数需要减去48之后,再加入到result中,然后将result * 10
3.不断循环,知道result的长度达到字符串的长度,或者result的值超越的整数的最大范围,则退出程序。

以下是API中的源码(省略其中转为其他进制数的代码):

Java代码 
  1. public static int parseInt( String s )  
  2.            throws NumberFormatException {  
  3.  if ( s == null ) {  
  4.      throw new NumberFormatException( "null" );  
  5.  }  
  6.    
  7.  int result = 0;  
  8.  boolean negative = false;  
  9.  int i = 0;  
  10.  int len = s.length();  
  11.  int limit = -Integer.MAX_VALUE;  
  12.  int multmin; //用来放置整数的最大范围  
  13.  int digit;  
  14.  int radix = 10;  
  15.    
  16.  if ( len > 0 ) {  
  17.      char firstChar = s.charAt( 0 );  
  18.      if ( firstChar < '0' ) { // Possible leading "+" or "-"  
  19.          if ( firstChar == '-' ) {  
  20.              negative = true;  
  21.              limit = Integer.MIN_VALUE;  
  22.          } else if ( firstChar != '+' )  
  23.              throw NumberFormatException.forInputString( s );  
  24.    
  25.          if ( len == 1 ) // Cannot have lone "+" or "-"  
  26.              throw NumberFormatException.forInputString( s );  
  27.          i++;  
  28.      }  
  29.      multmin = limit / radix;  
  30.      while ( i < len ) {  
  31.          // Accumulating negatively avoids surprises near MAX_VALUE  
  32.          digit = Character.digit( s.charAt( i++ ), radix ); //这一步应该是符合我的思路的第二条,我想如果应用上面的digits表应该也可以轻易做到  
  33.          if ( digit < 0 ) {  
  34.              throw NumberFormatException.forInputString( s );  
  35.          }  
  36.          if ( result < multmin ) {  
  37.              throw NumberFormatException.forInputString( s );  
  38.          }  
  39.          result *= radix;  
  40.          if ( result < limit + digit ) {  
  41.              throw NumberFormatException.forInputString( s );  
  42.          }  
  43.          result -= digit;  
  44.      }  
  45.  } else {  
  46.      throw NumberFormatException.forInputString( s );  
  47.  }  
  48.  return negative ? result : -result;  
  49. }  

摘要: 一般对于数字长度的判断,我们习惯上是采用模除的方法。使用递归的思想得到结果。以下是jdk源码中实现的思路:-----------------------------------------------------------------------------------------final st ...
一般对于数字长度的判断,我们习惯上是采用模除的方法。
使用递归的思想得到结果。
以下是jdk源码中实现的思路:
-----------------------------------------------------------------------------------------
final static int[] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
            99999999, 999999999, Integer.MAX_VALUE };
static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
    }
-----------------------------------------------------------------------------------------
递归实现的代码:
int num = random.nextInt();
            while (num / 10 > 0) {
                num = num / 10;
                length++;
            }
------------------------------------------------
当循环增加到百万数量级时,
前者的时间差不多是后者数量级的一半 。
压力有点大~
原文地址:https://www.cnblogs.com/daichangya/p/12960033.html