zoukankan      html  css  js  c++  java
  • 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++;
                }
    ------------------------------------------------
    当循环增加到百万数量级时,
    前者的时间差不多是后者数量级的一半 。
    压力有点大~
  • 相关阅读:
    通过WebService跨平台上传大文件到服务器
    C# 委托、Lambda表达式和事件——学习总结
    WIN8、WIN7访问Windows Server 2003服务器的数据库速度很慢、远程速度很慢的解决方法
    C#中接口与抽象类的区别
    DataGridView重查后,返回原来所在行
    需要开拓的领域
    FTP使用心得
    VS的几个实用快捷键
    C#中窗体、MDI的使用心得
    水晶报表使用心得
  • 原文地址:https://www.cnblogs.com/daichangya/p/12960033.html
Copyright © 2011-2022 走看看