读JavaAPI源代码——整数和字符串间的转换

读JavaAPI源代码——整数和字符串间的转换

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

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

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

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

/** * @param i 被转十进制数 * @param index 这个十进制数的总位数 * @param buf 用来存储转化后的结果 */ 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; r = i - ( ( q << 6 ) + ( q << 5 ) + ( q << 2 ) ); // 高效实现 r = i - ( q * 100 ); 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 ); //实现 q = i / 10(其中使用整数乘法后再移位来代替浮点数的乘法(q * 0.1),但对于现在的CPU来说不 //知是否还是整数乘法较快?) 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; } }

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

/** * All possible chars for representing a number as a String */ 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 [] 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', } ;

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

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

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; } 好了,现在是时候来看一下toString()方法了 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( 0, size, buf ); }

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

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

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

public static int parseInt( String s ) throws NumberFormatException { if ( s == null ) { throw new NumberFormatException( "null" ); } int result = 0; boolean negative = false; int i = 0; int len = s.length(); int limit = -Integer.MAX_VALUE; int multmin; //用来放置整数的最大范围 int digit; int radix = 10; if ( len > 0 ) { char firstChar = s.charAt( 0 ); if ( firstChar < '0' ) { // Possible leading "+" or "-" if ( firstChar == '-' ) { negative = true; limit = Integer.MIN_VALUE; } else if ( firstChar != '+' ) throw NumberFormatException.forInputString( s ); if ( len == 1 ) // Cannot have lone "+" or "-" throw NumberFormatException.forInputString( s ); i++; } multmin = limit / radix; while ( i < len ) { // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit( s.charAt( i++ ), radix ); //这一步应该是符合我的思路的第二条,我想如果应用上面的digits表应该也可以轻易做到 if ( digit < 0 ) { throw NumberFormatException.forInputString( s ); } if ( result < multmin ) { throw NumberFormatException.forInputString( s ); } result *= radix; if ( result < limit + digit ) { throw NumberFormatException.forInputString( s ); } result -= digit; } } else { throw NumberFormatException.forInputString( s ); } return negative ? result : -result; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值