我们经常都听有经验的人说,避免使用String通过“+”连接字符串,特
别是连接的次数很多的时候,一定要用StringBuffer。
测试代码:
package com.panther.demo.exercise6;
/**
* String,StringBuffer,StringBuilder字符串拼接效率对比
* Created by panther.dongdong on 2016/3/10.
*/
public class StringConnectTimeDemo {
private final int n = 200000;
public static void main(String[] args) {
StringConnectTimeDemo stringConnectTimeDemo = new StringConnectTimeDemo();
stringConnectTimeDemo.stringTest();
stringConnectTimeDemo.stringBufferTest();
stringConnectTimeDemo.stringBuilderTest();
}
public void stringTest() {
long start = System.currentTimeMillis();
String s = "";
for (int i = 0; i < n; ++i) {
s += i;
}
System.out.println("string cost time:" + (System.currentTimeMillis() - start));
}
public void stringBufferTest() {
long start = System.currentTimeMillis();
StringBuffer s = new StringBuffer("");
for (int i = 0; i < n; ++i) {
s.append(i);
}
System.out.println("stringBuffer cost time:" + (System.currentTimeMillis() - start));
}
public void stringBuilderTest() {
long start = System.currentTimeMillis();
StringBuilder s = new StringBuilder("");
for (int i = 0; i < n; ++i) {
s.append(i);
}
System.out.println("stringBuilder cost time:" + (System.currentTimeMillis() - start));
}
}
当拼接次数打到200000时,效果就非常明显了,
结果:
由上边的图可以清楚的看出,为什么大家都鼓励用StringBuffer连接字符串了。在连接次数少
的情况下,String的低效率表现并不是很突出,但是一旦连接次数多的时候,性能影响是很大的,String进
行20万次字符串的连接,大约需要45s时间,而StringBuffer只需要10毫秒,相差接近5000倍以上。而
StringBuffer和StringBuilder差别并不大,StringBuilder比StringBuffer稍微快点,我想是因为StringBuffer
是线程序安全的,StringBuilder不是线程序安全的,所以StringBuffer稍微慢点。
但是为什么String如此慢呢,分析以下代码
String result="";
result+="ok";
这段代码性能很低,原因是java中的String类不可变的(immutable),
通过使用javap工具我们可以知道其实上面的代码在编译成字节码的时候等同的源代码是:
String result="";
StringBuffer temp=new StringBuffer();
temp.append(result);
temp.append("ok");
result=temp.toString();
短短的两个语句怎么呢变成这么多呢?问题的原因就在String类的不可变性上。
所以,如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后
需要String,那么使用StringBuffer的toString()方法好了。但是 StringBuilder 的实例用于多个线程是不安
全的。如果需要这样的同步,则建议使用 StringBuffer,因为StringBuffer是线程安全的。在大多数非多
线程的开发中,为了提高效率,可以采用StringBuilder代替StringBuffer,速度更快。
补充:
public static String concat1(String s1, String s2, String s3, String s4, String s5, String s6) {
String result = "";
result += s1;
result += s2;
result += s3;
result += s4;
result += s5;
result += s6;
return result;
}
public static String concat2(String s1, String s2, String s3, String s4, String s5, String s6) {
StringBuffer result = new StringBuffer();
result.append(s1);
result.append(s2);
result.append(s3);
result.append(s4);
result.append(s5);
result.append(s6);
return result.toString();
}
public static String concat3(String s1, String s2, String s3, String s4, String s5, String s6) {
return new StringBuffer(s1.length() + s2.length() + s3.length() + s4.length() + s5.length() + s6.length())
.append(s1).append(s2).append(s3).append(s4).append(s5).append(s6).toString();
}
public static String concat4(String s1, String s2, String s3, String s4, String s5, String s6) {
return s1 + s2 + s3 + s4 + s5 + s6;
}
public static String concat5(String s1, String s2, String s3, String s4, String s5, String s6) {
return new StringBuilder(s1.length() + s2.length() + s3.length() + s4.length() + s5.length() + s6.length())
.append(s1).append(s2).append(s3).append(s4).append(s5).append(s6).toString();
}
第一种写法是最土的写法,也最累赘,事实上看到这样的代码我都会有点头疼。
看过《Effective Java》的朋友都知道用StringBuffer吧,用第二种写法的人应该也不少。
第4种写法当然最简捷,最优美的了,就是不知道性能怎么样。
Java 5里加了个StringBuilder类,与StringBuffer功能一样,就是没有同步,
所有用StringBuilder代替StringBuffer肯定对性能有好处,这样就产生的第5种写法。
还是做个测试有说服力。我的机器上同时装了JDK 5和JDK 6,两个都测了一下。
执行每个函数10000000次(输入的每个参数都是"a"),各种写法用时如下,单位毫秒:
JDK 5:
concat1: 13776
concat2: 5081
concat3: 4944
concat4: 4202
concat5: 4047
JDK 6:
concat1: 11801
concat2: 3930
concat3: 3976
concat4: 3353
concat5: 3440
可以看出第1种写法果然最慢,第二种写法由于用了StringBuffer,快了很多。
奇怪的是第4种写法竟然也很快,比用StringBuffer还快,怎么回事?
其实如果你调试过字符串连接的执行过程就会知道当用第4种写法时Java会自动使用StringBuilder.append()函数来进行连接。
所以最简捷的第4种写法已经够快了。
在JDK 5里,第5种写法最快,因为在创建StringBuilder的时候预先计算了总长度,消除了内存重分配。
不过没有必要这么写,JDK 6里已经为第4种写法做了更好的优化,第5种写法反而慢了。