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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM