关键字: java中字符串连接效率的测试


比较JAVA中String ,StringBuffer,SrtingBuilder三个对象连接字符串的效率。

我们经常都听有经验的人说,避免使用String通过“+”连接字符串,特
别是连接的次数很多的时候,一定要用StringBuffer。

比较下究竟谁的效率高,下边是测试代码,可直接运行:

package cn.com.test; 
 

public class TestStringConnectTime { 
 
    
 
    //连接时间的设定 
 
    private final int n = 20000; 
 
    
 
    public static void main(String[] args){ 
 
       TestStringConnectTime test = new TestStringConnectTime (); 
 
       test.testStringTime(); 
 
       test.testStringBufferTime(); 
 
       test.testStringBuilderTime(); 
 
    } 
 
    
 
    /** 
 
     *测试String连接字符串的时间 
 
     */ 
 
    public void testStringTime(){ 
 
       long start = System.currentTimeMillis(); 
 
       String a = ""; 
 
       for(int k=0;k<n;k++ ){ 
 
           a += "_" + k; 
 
       } 
 
       long end = System.currentTimeMillis(); 
 
       long time = end - start; 
 
       System.out.println("String time:" + time); 
 
    } 
 
    
 
    /** 
 
     *测试StringBuffer连接字符串的时间 
 
     */ 
 
    public void testStringBufferTime(){ 
 
       long start = System.currentTimeMillis(); 
 
       StringBuffer b = new StringBuffer() ; 
 
       for(int k=0;k<n;k++ ){ 
 
           b.append( "_" + k ); 
 
       } 
 
       long end = System.currentTimeMillis(); 
 
       long time = end - start; 
 
       System.out.println("StringBuffer time:" + time); 
 
    } 
 
    
 
    /** 
 
     *测试StringBuilder连接字符串的时间 
 
     */ 
 
    public void testStringBuilderTime(){ 
 
       long start = System.currentTimeMillis(); 
 
       StringBuilder c = new StringBuilder() ; 
 
       for(int k=0;k<n;k++ ){ 
 
           c.append( "_" + k ); 
 
       } 
 
       long end = System.currentTimeMillis(); 
 
       long time = end - start; 
 
       System.out.println("StringBuilder time:" + time); 
 
    } 
 

}




分别测试了n=10,100,500,1000,5000,10000,20000的时候,三个对象连接字符串所花费的时间,
做了个简单统计,得到如下数据:

连接次数(n) 所需时间(单位毫秒)
      String StringBuffer StringBuilder
10       0        0           0
100      0        0           0
500      31       16          0
1000     63       31          16
5000     781      63          47
10000    7547     63          62
20000    62984    94          63

由上边的图表结果对比,可以清楚的看出,为什么大家都鼓励用StringBuffer连接字符串了。在连接次数少
的情况下,String的低效率表现并不是很突出,但是一旦连接次数多的时候,性能影响是很大的,String进
行2万次字符串的连接,大约需要1分钟时间,而StringBuffer只需要94毫秒,相差接近500倍以上。而
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,速度更快。

补1:

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种写法反而慢了。