1. 程式人生 > >深度透析String與StringBuilder

深度透析String與StringBuilder

String的不可變性帶來的一定效率問題。

public class Concatenation {
     public static void main(String[] args) {
         String mango="mango";
         String s="abc"+mango+"def"+47;
         System.out.println(res);
     }
}

這種“+”會產生一大堆需要垃圾回收的中間物件。可以通過jdk自帶的javap來反編譯以上程式碼,就知道以上程式碼如何工作的。(為關注有效部分,JVM位元組碼經過有效簡化,下同)

D:\Download>javap -c Concatenation.class
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String mango
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String abc 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_1 16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def 21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: bipush 47 26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: astore_2 33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 36: aload_2 37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: return }

這-c標誌表示將生成JVM位元組碼,其中dup和invokevirtual語句相當於Java虛擬機器上的彙編語句,可以看出重點是:編譯器自動引入了java.lang.StringBuilder類。雖然我們原始碼總沒有加入StringBuilder類,因為它高效,編譯器自動使用了它。從上邊看出編譯器建立了一個StringBuilder物件,用以構建最終的String物件,併為每個字串呼叫一次StringBuilider的append()方法,總計4次。最後呼叫StringBuilder的toString()方法生成結果,並存為s(使用的命令是astore_2)。
要有對比才能看出效能的區別,

public class WhitherStringBuilder {
     public String implicit(String[] fields) {
         String result = "";
         for (int i = 0; i < fields.length; i++) {
              result += fields[i];
         }
         return result;
     }
     public String explicit(String[] fields) {
         StringBuilder result = new StringBuilder();
         for (int i = 0; i < fields.length; i++) {
              result.append(fields[i]);
         }
         return result.toString();
     }
}
D:\Download>javap -c WhitherStringBuilder.class
 public java.lang.String implicit(java.lang.String[]);
    Code:
       0: ldc           #2                  // String
       2: astore_2
       3: iconst_0
       4: istore_3
       5: iload_3
       6: aload_1
       7: arraylength
       8: if_icmpge     38
      11: new           #3                  // class java/lang/StringBuilder
      14: dup
      15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      18: aload_2
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: aload_1
      23: iload_3
      24: aaload
      25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: astore_2
      32: iinc          3, 1
      35: goto          5
      38: aload_2
      39: areturn

  public java.lang.String explicit(java.lang.String[]);
    Code:
       0: new           #3                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
       7: astore_2
       8: iconst_0
       9: istore_3
      10: iload_3
      11: aload_1
      12: arraylength
      13: if_icmpge     30
      16: aload_2
      17: aload_1
      18: iload_3
      19: aaload
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: iinc          3, 1
      27: goto          10
      30: aload_2
      31: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: areturn
}

首先看implicit()方法,第8行到第35行構成一個迴圈體。第8行:隊堆疊中的運算元進行“大於或等於的整數比較運算”,迴圈結束時跳到第38行。第35行:返回迴圈體的起始點(第5行)。可以看到StringBuilder在迴圈體內構造,這就意味著沒經過一次迴圈就會產生一個新的StringBuilder物件。
再來看explicit()方法,這個方法的不僅迴圈程式碼更簡短,更簡單,而且它只生成了一個StringBuilder物件。同時顯式地建立StringBuilder還允許預先為其指定大小,準確知道大小,預先分好,避免多次重新分配緩衝。
因此,字串比較簡單,可使用String類,要是比較複雜或在迴圈體內使用建議使用StringBuilder類。