java String、String.concat和StringBuilder性能对比

看到网上有人已经做过对比,并且贴出了代码,然后我运行了之后发现跟我分析的结论差距很大。发现他的代码有个问题,UUID.randomUUID() 首次调用耗时会很高,这个耗时被计算给了String,这对String是不公平的。

原始代码参见:http://www.codes51.com/article/detail_99554.html

修改后的测试代码如下:

import java.util.Date;
import java.util.UUID;

public class StringTest {
    
    public static void main(String[] args) {
        int testLength = 10000;
        String[] arr = new String[2];
        String str = "";
        
        Date start = new Date();
        String testStr = UUID.randomUUID().toString();
        System.out.println("首次生成randomUUID耗时:" + (new Date().getTime() - start.getTime()));
        
        start = new Date();
        for (int i = 0; i < testLength; i++) {
            testStr = UUID.randomUUID().toString();
        }
        System.out.println("非首次生成randomUUID " + testLength + "次耗时:" + (new Date().getTime() - start.getTime()));
        
        start = new Date();
        for (int i = 0; i < testLength; i++) {
            str = testStr + testStr;
        }
        System.out.println("String 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() - start.getTime()));
        
        start = new Date();
        for (int i = 0; i < testLength; i++) {
            str = testStr.concat(testStr);
        }
        System.out.println("String.concat 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() - start.getTime()));

        start = new Date();
        StringBuilder sb;
        for (int i = 0; i < testLength; i++) {
            str = "";
            sb = new StringBuilder();
            for (int j = 0; j < arr.length; j++) {
                sb.append(testStr);
            }
            str = sb.toString();
        }
        System.out.println("StringBuilder 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() - start.getTime()));
    }
}

 

测试结果:

1. 测试字符串数组长度10

首次生成randomUUID耗时:290
非首次生成randomUUID 10000次耗时:44
String 拼接测试,测试长度10000,测试字符串数组长度10,完成时间14
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度10,完成时间66
String.concat 拼接测试,测试长度10000,测试字符串数组长度10,完成时间14
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度10,完成时间14

 

2. 测试字符串数组长度5

首次生成randomUUID耗时:287
非首次生成randomUUID 10000次耗时:48
String 拼接测试,测试长度10000,测试字符串数组长度5,完成时间11
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度5,完成时间20
String.concat 拼接测试,测试长度10000,测试字符串数组长度5,完成时间9
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度5,完成时间10

 

3. 测试字符串数组长度3

首次生成randomUUID耗时:308
非首次生成randomUUID 10000次耗时:35
String 拼接测试,测试长度10000,测试字符串数组长度3,完成时间10
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度3,完成时间21
String.concat 拼接测试,测试长度10000,测试字符串数组长度3,完成时间6
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度3,完成时间11

 

4. 测试字符串数组长度2

首次生成randomUUID耗时:298
非首次生成randomUUID 10000次耗时:70
String 拼接测试,测试长度10000,测试字符串数组长度2,完成时间10
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度2,完成时间8
String.concat 拼接测试,测试长度10000,测试字符串数组长度2,完成时间3
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度2,完成时间7

 

5. 测试字符串数组长度1

首次生成randomUUID耗时:278
非首次生成randomUUID 10000次耗时:71
String 拼接测试,测试长度10000,测试字符串数组长度1,完成时间1
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度1,完成时间8
String.concat 拼接测试,测试长度10000,测试字符串数组长度1,完成时间3
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度1,完成时间4

 

到此,可以看出,绝大多数情况下StringBuilder妥妥的比String 使用循环快,但是跟String直接相加差不多,String concat效率跟StringBuilder差不多,很多时候还要快一些,这些都是为什么呢?

javap -c StringTest.class 看看Java编译器都做了什么:

Compiled from "StringTest.java"
public class StringTest {
  public StringTest();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: sipush        10000
       3: istore_1      
       4: iconst_2      
       5: anewarray     #2                  // class java/lang/String
       8: astore_2      
       9: ldc           #3                  // String 
      11: astore_3      
      12: new           #4                  // class java/util/Date
      15: dup           
      16: invokespecial #5                  // Method java/util/Date."<init>":()V
      19: astore        4
      21: invokestatic  #6                  // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
      24: invokevirtual #7                  // Method java/util/UUID.toString:()Ljava/lang/String;
      27: astore        5
      29: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      32: new           #9                  // class java/lang/StringBuilder
      35: dup           
      36: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
      39: ldc           #11                 // String 首次生成randomUUID耗时:
      41: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      44: new           #4                  // class java/util/Date
      47: dup           
      48: invokespecial #5                  // Method java/util/Date."<init>":()V
      51: invokevirtual #13                 // Method java/util/Date.getTime:()J
      54: aload         4
      56: invokevirtual #13                 // Method java/util/Date.getTime:()J
      59: lsub          
      60: invokevirtual #14                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      63: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      66: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      69: new           #4                  // class java/util/Date
      72: dup           
      73: invokespecial #5                  // Method java/util/Date."<init>":()V
      76: astore        4
      78: iconst_0      
      79: istore        6
      81: iload         6
      83: iload_1       
      84: if_icmpge     101
      87: invokestatic  #6                  // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
      90: invokevirtual #7                  // Method java/util/UUID.toString:()Ljava/lang/String;
      93: astore        5
      95: iinc          6, 1
      98: goto          81
     101: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
     104: new           #9                  // class java/lang/StringBuilder
     107: dup           
     108: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     111: ldc           #17                 // String 非首次生成randomUUID 
     113: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     116: iload_1       
     117: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     120: ldc           #19                 // String 次耗时:
     122: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     125: new           #4                  // class java/util/Date
     128: dup           
     129: invokespecial #5                  // Method java/util/Date."<init>":()V
     132: invokevirtual #13                 // Method java/util/Date.getTime:()J
     135: aload         4
     137: invokevirtual #13                 // Method java/util/Date.getTime:()J
     140: lsub          
     141: invokevirtual #14                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
     144: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     147: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     150: new           #4                  // class java/util/Date
     153: dup           
     154: invokespecial #5                  // Method java/util/Date."<init>":()V
     157: astore        4
     159: iconst_0      
     160: istore        6
     162: iload         6
     164: iload_1       
     165: if_icmpge     195
     168: new           #9                  // class java/lang/StringBuilder
     171: dup           
     172: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     175: aload         5
     177: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     180: aload         5
     182: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     185: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     188: astore_3      
     189: iinc          6, 1
     192: goto          162
     195: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
     198: new           #9                  // class java/lang/StringBuilder
     201: dup           
     202: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     205: ldc           #20                 // String String 拼接测试,测试长度
     207: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     210: iload_1       
     211: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     214: ldc           #21                 // String ,测试字符串数组长度
     216: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     219: aload_2       
     220: arraylength   
     221: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     224: ldc           #22                 // String ,完成时间
     226: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     229: new           #4                  // class java/util/Date
     232: dup           
     233: invokespecial #5                  // Method java/util/Date."<init>":()V
     236: invokevirtual #13                 // Method java/util/Date.getTime:()J
     239: aload         4
     241: invokevirtual #13                 // Method java/util/Date.getTime:()J
     244: lsub          
     245: invokevirtual #14                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
     248: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     251: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     254: new           #4                  // class java/util/Date
     257: dup           
     258: invokespecial #5                  // Method java/util/Date."<init>":()V
     261: astore        4
     263: iconst_0      
     264: istore        6
     266: iload         6
     268: iload_1       
     269: if_icmpge     317
     272: ldc           #3                  // String 
     274: astore_3      
     275: iconst_0      
     276: istore        7
     278: iload         7
     280: aload_2       
     281: arraylength   
     282: if_icmpge     311
     285: new           #9                  // class java/lang/StringBuilder
     288: dup           
     289: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     292: aload_3       
     293: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     296: aload         5
     298: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     301: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     304: astore_3      
     305: iinc          7, 1
     308: goto          278
     311: iinc          6, 1
     314: goto          266
     317: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
     320: new           #9                  // class java/lang/StringBuilder
     323: dup           
     324: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     327: ldc           #23                 // String String 使用循环 拼接测试,测试长度
     329: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     332: iload_1       
     333: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     336: ldc           #21                 // String ,测试字符串数组长度
     338: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     341: aload_2       
     342: arraylength   
     343: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     346: ldc           #22                 // String ,完成时间
     348: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     351: new           #4                  // class java/util/Date
     354: dup           
     355: invokespecial #5                  // Method java/util/Date."<init>":()V
     358: invokevirtual #13                 // Method java/util/Date.getTime:()J
     361: aload         4
     363: invokevirtual #13                 // Method java/util/Date.getTime:()J
     366: lsub          
     367: invokevirtual #14                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
     370: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     373: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     376: new           #4                  // class java/util/Date
     379: dup           
     380: invokespecial #5                  // Method java/util/Date."<init>":()V
     383: astore        4
     385: iconst_0      
     386: istore        6
     388: iload         6
     390: iload_1       
     391: if_icmpge     408
     394: aload         5
     396: aload         5
     398: invokevirtual #24                 // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
     401: astore_3      
     402: iinc          6, 1
     405: goto          388
     408: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
     411: new           #9                  // class java/lang/StringBuilder
     414: dup           
     415: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     418: ldc           #25                 // String String.concat 拼接测试,测试长度
     420: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     423: iload_1       
     424: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     427: ldc           #21                 // String ,测试字符串数组长度
     429: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     432: aload_2       
     433: arraylength   
     434: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     437: ldc           #22                 // String ,完成时间
     439: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     442: new           #4                  // class java/util/Date
     445: dup           
     446: invokespecial #5                  // Method java/util/Date."<init>":()V
     449: invokevirtual #13                 // Method java/util/Date.getTime:()J
     452: aload         4
     454: invokevirtual #13                 // Method java/util/Date.getTime:()J
     457: lsub          
     458: invokevirtual #14                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
     461: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     464: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     467: new           #4                  // class java/util/Date
     470: dup           
     471: invokespecial #5                  // Method java/util/Date."<init>":()V
     474: astore        4
     476: iconst_0      
     477: istore        7
     479: iload         7
     481: iload_1       
     482: if_icmpge     533
     485: ldc           #3                  // String 
     487: astore_3      
     488: new           #9                  // class java/lang/StringBuilder
     491: dup           
     492: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     495: astore        6
     497: iconst_0      
     498: istore        8
     500: iload         8
     502: aload_2       
     503: arraylength   
     504: if_icmpge     521
     507: aload         6
     509: aload         5
     511: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     514: pop           
     515: iinc          8, 1
     518: goto          500
     521: aload         6
     523: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     526: astore_3      
     527: iinc          7, 1
     530: goto          479
     533: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
     536: new           #9                  // class java/lang/StringBuilder
     539: dup           
     540: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
     543: ldc           #26                 // String StringBuilder 拼接测试,测试长度
     545: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     548: iload_1       
     549: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     552: ldc           #21                 // String ,测试字符串数组长度
     554: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     557: aload_2       
     558: arraylength   
     559: invokevirtual #18                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     562: ldc           #22                 // String ,完成时间
     564: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     567: new           #4                  // class java/util/Date
     570: dup           
     571: invokespecial #5                  // Method java/util/Date."<init>":()V
     574: invokevirtual #13                 // Method java/util/Date.getTime:()J
     577: aload         4
     579: invokevirtual #13                 // Method java/util/Date.getTime:()J
     582: lsub          
     583: invokevirtual #14                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
     586: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     589: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     592: return        
}

 

String直接相加已经都被编译器优化成StringBuilder了,只是循环里优化的不太合理。所以,String相加快还是StringBuilder快,其实只是StringBuilder调用方式对比。。。

String.concat为什么快?

看看jdk1.8里这个方法的源代码就知道了:

public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

简单粗暴,直接Arrays.copyOf,直接内存复制,这根StringBuilder原理类似,但是它不用初始化StringBuilder对象,只是每次concat都会创建一个新的String对象,所以在有些情况下它比StringBuilder要快一点。

 

结论:简单场景里,直接用+好了,反正编译器默认会优化成StringBuilder,毕竟对一般人来说加号可读性高一点。但是在循环中使用或者是比较复杂的应用场景里,还是尽量自己直接用StringBuilder或String concat。

posted on 2016-06-20 17:42  oceanking  阅读(5609)  评论(0编辑  收藏  举报