细节决定成败,开发中往往从一些细节就可以看出一个程序员的开发水准,下面我就给大家分享一下开发中最最常见的int转换为String类型的方法及其性能解析。
一般大家最常用的方法有
方法一:String s1 = String.valueOf(i);
方法二:String s2 = i+"";
不知道有没有人用这种方法呢?
方法三:String s3 = Integer.toString(i);
继续往下看之前,大家先猜测一下这三种方法哪种方法的效率最高,耗时最短,对内存消耗最小?相信结果会令你大吃一惊!
话不多说,直接上代码,用事实说话。
package com.zhu.test;public class IntToStringOptimize {public static void main(String[] args) {//1.String.valueOf(i)方式long t = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {String s0 = String.valueOf(i);}System.out.println("String.valueOf(i)方式耗时:" + (System.currentTimeMillis() - t));//2.i+""方式t = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {String s = "" + i;}System.out.println("i+ \"\" 方式耗时 :" + (System.currentTimeMillis() - t));//3.Integer.toString(i)方式t = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {String s = Integer.toString(i);}System.out.println("Integer.toString(i)方式耗时:" + (System.currentTimeMillis() - t));} }
运行结果如下:
结果是不是大跌眼镜啊?没想到我们最常用的i+""的性能竟然如此之差!而性能最好的竟然是没人怎么用的toString(i);为什么会这样呢?经过堆栈分析发现:
String.valueOf(i)的方法调用的竟然时第三种方法:Integer.toString(i),多此调试后发现他们的耗时比基本保持在20:8,那么toString(i)的内部又是怎样实现的呢?
下面是Integer.toString(i)的实现代码:
1 public static String toString(int i) { 2 if (i == Integer.MIN_VALUE) 3 return "-2147483648"; 4 int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); 5 char[] buf = new char[size]; 6 getChars(i, size, buf); 7 return new String(buf, true); 8 }
其中stringSize(i)又做了什么事呢?经过进一步跟踪发现
1 static int stringSize(int x) { 2 for (int i=0; ; i++) 3 if (x <= sizeTable[i]) 4 return i+1; 5 }
而sizeTable[]又是一个怎样的数组呢?继续往下看,
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,99999999, 999999999, Integer.MAX_VALUE };
原来在调用toString(i)的时候,如果会判断i是否是负数,如果是负数就将其求反为正数,然后会根据 sizeTable数组来 判断 i 的位数并返回创建一个比i的长度+1的字符数组,比如i=11,那么size就是3,然后就会创建一个3位的字符数组。那么问题来了,getChars()有是干什么的呢?
1 /** 2 * Places characters representing the integer i into the 3 * character array buf. The characters are placed into 4 * the buffer backwards starting with the least significant 5 * digit at the specified index (exclusive), and working 6 * backwards from there. 7 * 8 * Will fail if i == Integer.MIN_VALUE 9 */ 10 static void getChars(int i, int index, char[] buf) { 11 int q, r; 12 int charPos = index; 13 char sign = 0; 14 15 if (i < 0) { 16 sign = '-'; 17 i = -i; 18 } 19 20 // Generate two digits per iteration 21 while (i >= 65536) { 22 q = i / 100; 23 24 r = i - ((q << 6) + (q << 5) + (q << 2)); 25 i = q; 26 buf [--charPos] = DigitOnes[r]; 27 buf [--charPos] = DigitTens[r]; 28 } 29 30 // Fall thru to fast mode for smaller numbers 31 // assert(i <= 65536, i); 32 for (;;) { 33 q = (i * 52429) >>> (16+3); 34 r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... 35 buf [--charPos] = digits [r]; 36 i = q; 37 if (i == 0) break; 38 } 39 if (sign != 0) { 40 buf [--charPos] = sign; 41 } 42 }
仔细分析代码发现,这个函数的功能就是将int型的i从右向左(即从个位数开始)填充到字符数组buf中。至此方法一String.valueOf(i)和方法三Integer.toString(i)分析完毕。
由上可见,方式二 i+"" 是最耗时耗内存的方法,之所以写这篇文章是因为我在看一段视频的时候以为老师说他以前刚入职的时候就是用这种方法从而项目中出现大量的+"",结果是被项目经理批评了一顿。所以小伙伴们,如果你还在用方法二就赶快更正过来吧!
那么方法二为什么会这么耗时呢?
因为每 +"" 一次,就会调用一次
public StringBuffer() {
super(16);
}
方法,这就意味着每 +"" 一次,就会在内存中实例化一个StringBuffer()对象,原因是String类型是final的,其内容是不可变的,所以每次改变其值就要重新new一个对象,如果一个项目中大量使用该方法,不耗时耗内存才怪呢。
个人总结:看到这里相信大家都知道了到底哪种方法才是最有效的,int类型转为String类型时使用Integer.toString(i)或String.valueOf(i)方法会比+""高效节能的多。希望阅读此文能提升一下读者的逼格,如果有哪个地方我分析的不对或者有什么更好的建议或更实用的细节还请小伙伴们不吝赐教!