如果让您亲自动手来来写一个Integer中的toString()方法和parseInt()方法,您会怎么写?
请您先动手写toString()方法。
我对于toString()的思路:
1.首先需要得到这个Integer数值的位数,所以肯定有一个getIntegerSize()的private方法
2.然后要把这个Integer数值转化为一个字符数组,所以也要有一个getChars()的private方法
3.最后只要把这个字符数组转化为字符串返回即可
在编写完我们的代码,再确定对于现在自己的实力,已经没有再做优化的可能之后,再来看Integer中的toString()来领教一下高手的代码是怎么写的。
下面是这个过程中最重要的一步:将数值转成字符数组;
其中用使用来三个表(DigitOnes、DigitTens、digits)作为表驱动法(在代码大全上看到过,但还没用过:P)的快速索引来加快代码的执行效率。
[java] view plaincopyprint?
01./**
02. * @param i 被转十进制数
03. * @param index 这个十进制数的总位数
04. * @param buf 用来存储转化后的结果
05. */
06.static void getChars(int i, int index, char[] buf) {
07. int q, r;
08. int charPos = index;
09. 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.}
/**
* @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来说不 //知是否还是整数乘法较快?)【成都java培训】
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()方法中还要加入预防措施。
[java] view plaincopyprint?
01./**
02. * All possible chars for representing a number as a String
03. */
04. final static char[] digits = {
05. \'0\' , \'1\' , \'2\' , \'3\' , \'4\' , \'5\' ,
06. \'6\' , \'7\' , \'8\' , \'9\' , \'a\' , \'b\' ,
07. \'c\' , \'d\' , \'e\' , \'f\' , \'g\' , \'h\' ,
08. \'i\' , \'j\' , \'k\' , \'l\' , \'m\' , \'n\' ,
09. \'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. } ;
/**
* 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:【成都java培训】
[java] view plaincopyprint?
01.final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
02. 99999999, 999999999, Integer.MAX_VALUE };
03.
04. // Requires positive x
05. static int stringSize( int x ) {
06. for ( int i=0; ; i++ )
07. if ( x <= sizeTable[i] )
08. return i+1;
09. }
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. }
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中的源码(省略其中转为其他进制数的代码):
[java] view plaincopyprint?
01.public static int parseInt( String s )
02. throws NumberFormatException {
03. if ( s == null ) {
04. throw new NumberFormatException( "null" );
05. }
06.
07. int result = 0;
08. boolean negative = false;
09. 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.}