/**
* String连接性能测试
*@author x1ny
*@date 2015/8/8
*/
根据String 的字节码实现知道 使用 “+”连接字符串实际上是创建了一个StringBuffer对象进行连接操作然后再转换为String对象。下面将在具体测试程序中测试性能的差异。
测试方法:用四种不同的连接方式 创建“tsettesttesttesttest”字符串1千万次,查看其性能差别
测试代码:
public class StringConnectionTest {
public static void main(String[] args) {
connByOperationOnce(10000000);
connByOperation(10000000);
connByStringBuffer(10000000);
connByStringBuilder(10000000);
}
public static void connByOperationOnce(int count){
long endTime;
long time;
long startTime = System.currentTimeMillis();
String str;
for(int i = 0; i <= count; i++)
str = "test" + "test" + "test" + "test" + "test";
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用加号一次连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
public static void connByOperation(int count){
long endTime;
long time;
long startTime = System.currentTimeMillis();
String str;
for(int i = 0; i <= count; i++){
str = "test";
str += "test";
str += "test";
str += "test";
str += "test";
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用加号多次连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
public static void connByStringBuffer(int count){
long endTime;
long time;
long startTime = System.currentTimeMillis();
String str;
for(int i = 0; i <= count; i++){
StringBuffer sb = new StringBuffer("test");
sb.append("test");
sb.append("test");
sb.append("test");
sb.append("test");
str = sb.toString();
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用StringBuffer连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
public static void connByStringBuilder(int count){
long endTime;
long time;
long startTime = System.currentTimeMillis();
String str;
for(int i = 0; i <= count; i++){
StringBuilder sb = new StringBuilder("test");
sb.append("test");
sb.append("test");
sb.append("test");
sb.append("test");
str = sb.toString();
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用StringBuilder连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
}
测试结果:
测试版本为1.6.0_65
使用加号一次连接字符串10000000次所需要的时间为:10ms
使用加号多次连接字符串10000000次所需要的时间为:3383ms
使用StringBuffer连接字符串10000000次所需要的时间为:1561ms
使用StringBuilder连接字符串10000000次所需要的时间为:1199ms
测试版本为1.7.0_79
使用加号一次连接字符串10000000次所需要的时间为:6ms
使用加号多次连接字符串10000000次所需要的时间为:2452ms
使用StringBuffer连接字符串10000000次所需要的时间为:2097ms
使用StringBuilder连接字符串10000000次所需要的时间为:864ms
测试版本为1.8.0_40
使用加号一次连接字符串10000000次所需要的时间为:5ms
使用加号多次连接字符串10000000次所需要的时间为:1782ms
使用StringBuffer连接字符串10000000次所需要的时间为:726ms
使用StringBuilder连接字符串10000000次所需要的时间为:840ms
发现使用 String str = "test" + "test" + "test" + "test" + "test" ; 性能与其他方式差距太大,几乎为0,查看字节码可以发现:
11: ldc #68 // String testtesttesttesttest
编译器在编译时直接将语句优化为 String str = "testtesttesttesttest'" 并没有进行连接操作,于是乎花费时间几乎为0。
测试将被连接的字符串“test‘用局部变量保存。
将测试代码改为:
package mytest;
public class StringConnectionTest {
public static void main(String[] args) {
System.out.println("测试版本为" + System.getProperty("java.version"));
connByOperationOnce(10000000);
connByOperation(10000000);
connByStringBuffer(10000000);
connByStringBuilder(10000000);
}
public static void connByOperationOnce(int count){
long endTime;
long time;
String str;
String str2 = "test";
long startTime = System.currentTimeMillis();
for(int i = 0; i <= count; i++)
str = str2 + str2 + str2 + str2 + str2;
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用加号一次连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
public static void connByOperation(int count){
long endTime;
long time;
String str;
String srt2 = "test";
long startTime = System.currentTimeMillis();
for(int i = 0; i <= count; i++){
str = srt2;
str += srt2;
str += srt2;
str += srt2;
str += srt2;
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用加号多次连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
public static void connByStringBuffer(int count){
long endTime;
long time;
String str;
String str2 = "test";
long startTime = System.currentTimeMillis();
for(int i = 0; i <= count; i++){
StringBuffer sb = new StringBuffer(str2);
sb.append(str2);
sb.append(str2);
sb.append(str2);
sb.append(str2);
str = sb.toString();
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用StringBuffer连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
public static void connByStringBuilder(int count){
long endTime;
long time;
String str;
String str2 = "test";
long startTime = System.currentTimeMillis();
for(int i = 0; i <= count; i++){
StringBuilder sb = new StringBuilder(str2);
sb.append(str2);
sb.append(str2);
sb.append(str2);
sb.append(str2);
str = sb.toString();
}
endTime = System.currentTimeMillis();
time = endTime - startTime;
System.out.println("使用StringBuilder连接字符串" + count + "次所需要的时间为:" + time + "ms" );
}
}
再进行测试得:
测试版本为1.6.0_65
使用加号一次连接字符串10000000次所需要的时间为:1366ms
使用加号多次连接字符串10000000次所需要的时间为:3433ms
使用StringBuffer连接字符串10000000次所需要的时间为:1449ms
使用StringBuilder连接字符串10000000次所需要的时间为:1139ms
测试版本为1.7.0_79
使用加号一次连接字符串10000000次所需要的时间为:1630ms
使用加号多次连接字符串10000000次所需要的时间为:3432ms
使用StringBuffer连接字符串10000000次所需要的时间为:1512ms
使用StringBuilder连接字符串10000000次所需要的时间为:1152ms
测试版本为1.8.0_40
使用加号一次连接字符串10000000次所需要的时间为:1233ms
使用加号多次连接字符串10000000次所需要的时间为:2780ms
使用StringBuffer连接字符串10000000次所需要的时间为:1175ms
使用StringBuilder连接字符串10000000次所需要的时间为:1085ms
结论:
当在线程不安全的环境下:
应该使用StringBuffer来确保线程安全。
在线程安全的环境下:
使用StringBuilder永远可以获取更高的性能, 但若在一条语句中连接,使用“+”号性能略低但可读性,可维护性更高。
待解决困惑: “+”是使用StringBuilder实现的,在一条语句中使用“+”号连接只创建了一个StringBulider,为什么性能和直接使用StringBulider差别颇大。