天天看點

一道String拼接的考題題目解析

題目

When doing string concatenation for many times in a loop, which is the fastest way in terms of executing time:

A) The concat() method of String

B) The + operator of String

C) The append() method of StringBuilder

D) The append() method of StringBuffer

解析

先看concat()的源碼

public String concat(String string) {
        if (string.count > 0 && count > 0) {
            char[] buffer = new char[count + string.count];
            System.arraycopy(value, offset, buffer, 0, count);
            System.arraycopy(string.value, string.offset, buffer, count, string.count);
            return new String(0, buffer.length, buffer);
        }
        return count == 0 ? string : this;
    }
           

再看“+ ”

public class ConcatString {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        String c = a + b;
    }
}
           

使用javap -c ConcatString.class,看看編譯器的做了什麼

public class com.nqy.mycode.test.ConcatString {
  public com.nqy.mycode.test.ConcatString();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1      
       3: ldc           #3                  // String b
       5: astore_2      
       6: new           #4                  // class java/lang/StringBuilder
       9: dup           
      10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      13: aload_1       
      14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      17: aload_2       
      18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      24: astore_3      
      25: return        
}
           

這裡,編譯器自己做了優化,使用StringBuilder的append來連接配接,然後傳回toString().

但是否如題目所說的,在一個循環中連接配接多個String,"+"和StringBuidler效率就一樣呢?

public class ConcatString {
    public static void main(String[] args) {
        String r = "";
        long s = System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            r+=String.valueOf(i);
        }
        System.out.println(r);
        System.out.println(System.currentTimeMillis() - s);

        StringBuilder sb = new StringBuilder();
        long s2 = System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            sb.append(String.valueOf(i));
        }
        System.out.println(sb.toString());
        System.out.println(System.currentTimeMillis() - s2);
    }
}
           

執行上面的代碼,“+”使用了時間305毫秒,StringBuilder使用了3毫秒。

看編譯器是怎麼做的

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

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 
       2: astore_1      
       3: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
       6: lstore_2      
       7: iconst_0      
       8: istore        4
      10: iload         4
      12: sipush        10000
      15: if_icmpge     47
      18: new           #4                  // class java/lang/StringBuilder
      21: dup           
      22: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      25: aload_1       
      26: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      29: iload         4
      31: invokestatic  #7                  // Method java/lang/String.valueOf:(I)Ljava/lang/String;
      34: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      37: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      40: astore_1      
      41: iinc          4, 1
      44: goto          10
      47: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      50: aload_1       
      51: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      54: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      57: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
      60: lload_2       
      61: lsub          
      62: invokevirtual #11                 // Method java/io/PrintStream.println:(J)V
      65: new           #4                  // class java/lang/StringBuilder
      68: dup           
      69: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      72: astore        4
      74: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
      77: lstore        5
      79: iconst_0      
      80: istore        7
      82: iload         7
      84: sipush        10000
      87: if_icmpge     107
      90: aload         4
      92: iload         7
      94: invokestatic  #7                  // Method java/lang/String.valueOf:(I)Ljava/lang/String;
      97: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     100: pop           
     101: iinc          7, 1
     104: goto          82
     107: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     110: aload         4
     112: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     115: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     118: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
     121: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
     124: lload         5
     126: lsub          
     127: invokevirtual #11                 // Method java/io/PrintStream.println:(J)V
     130: return        
}
           

每循環一次都建立一個StringBuidler去append,是以開銷很大,而直接使用StringBuilder每次循環,隻需要append下就可以。

看StringBuilder和StringBuffer的差別

public synchronized StringBuffer append(char ch) {
        append0(ch);
        return this;
    }
           
public StringBuilder append(char c) {
        append0(c);
        return this;
    }
           

StringBuffer每次append都會檢查所否線程安全,是以其效率比StringBuilder低。

是以,答案應是C,使用StringBuilder的append()