I found string plus using StringBuffer but not StringBuilder when I was implementing my simple JVM. So I wrote some test case to test it. But it became using StringBuilder but not StringBuffer. So I guess whether use StringBuffer or StringBuilder is depend on compiler option. Any way I got a performance test result between using StringBuffer and StringBuilder. The result is that using StringBuilder is much faster than using StringBuffer . The time consume ratio between using StringBuilder and StringBuffer is about 2:3 .
Here comes my test code.
public class StringAddTest implements Runnable { static long time1 = 0; static long time2 = 0; static long time3 = 0; /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new StringAddTest()); Thread thread2 = new Thread(new StringAddTest()); Thread thread3 = new Thread(new StringAddTest()); Thread thread4 = new Thread(new StringAddTest()); thread1.start(); thread2.start(); thread3.start(); thread4.start(); thread1.join(); thread2.join(); thread3.join(); thread4.join(); System.out.println("stringAdd1 spent " + time1 + " ms"); System.out.println("stringAdd2 spent " + time2 + " ms"); System.out.println("stringAdd3 spent " + time3 + " ms"); } public static void stringAdd1(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = a + b + i; } long spent = System.currentTimeMillis() - start; time1 += spent; System.out.println("TestStringAdd1 spent " + spent + "ms"); } public static void stringAdd2(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = new StringBuilder().append(a).append(b).append(i).toString(); } long spent = System.currentTimeMillis() - start; time2 += spent; System.out.println("TestStringAdd2 spent " + spent + "ms"); } public static void stringAdd3(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = new StringBuffer().append(a).append(b).append(i).toString(); } long spent = System.currentTimeMillis() - start; time3 += spent; System.out.println("TestStringAdd3 spent " + spent + "ms"); } public static void StringAddx() { String orignal = "Original"; orignal += "What"; } public void run() { int n = 1000 * 1000; for (int i = 0; i < 10; i++) { stringAdd1(n); stringAdd2(n); stringAdd3(n); stringAdd3(n); stringAdd2(n); stringAdd1(n); stringAdd2(n); stringAdd3(n); stringAdd1(n); } } }
I ran it for several times. The results are closed to each other. One test result on my machine is
DirectStringAdd spent 138057 ms StringBuilderAppend spent 152647 ms StringBufferAppend spent 256578 ms
From the result can see that use direct string plus is a little faster than use StringBuilder. I was trying to figure it out by analyze the binary code generated and add two more test case. Here comes my new test code.
public class StringAddTest implements Runnable { static long timeDirectStringAdd = 0; static long timeStringBuilderAppend = 0; static long timeStringBufferAppend = 0; static long timeStringBuilderAppend2 = 0; static long timeStringBufferAppend2 = 0; /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new StringAddTest()); Thread thread2 = new Thread(new StringAddTest()); Thread thread3 = new Thread(new StringAddTest()); Thread thread4 = new Thread(new StringAddTest()); thread1.start(); thread2.start(); thread3.start(); thread4.start(); thread1.join(); thread2.join(); thread3.join(); thread4.join(); System.out.println("DirectStringAdd spent " + timeDirectStringAdd + " ms"); System.out.println("StringBuilderAppend spent " + timeStringBuilderAppend + " ms"); System.out.println("StringBufferAppend spent " + timeStringBufferAppend + " ms"); System.out.println("StringBuilderAppend2 spent " + timeStringBuilderAppend2 + " ms"); System.out.println("StringBufferAppend2 spent " + timeStringBufferAppend2 + " ms"); } public static void directStringAdd(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = a + b + i; } long spent = System.currentTimeMillis() - start; timeDirectStringAdd += spent; System.out.println("DirectStringAdd spent " + spent + "ms"); } public static void stringBuilderAppend(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = new StringBuilder().append(a).append(b).append(i).toString(); } long spent = System.currentTimeMillis() - start; timeStringBuilderAppend += spent; System.out.println("StringBufferAppend spent " + spent + "ms"); } public static void stringBuilderAppend2(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = new StringBuilder(a).append(b).append(i).toString(); } long spent = System.currentTimeMillis() - start; timeStringBuilderAppend2 += spent; System.out.println("StringBufferAppend2 spent " + spent + "ms"); } public static void stringBufferAppend(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = new StringBuffer().append(a).append(b).append(i).toString(); } long spent = System.currentTimeMillis() - start; timeStringBufferAppend += spent; System.out.println("StringBuilderAppend spent " + spent + "ms"); } public static void stringBufferAppend2(int n) { long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { String a = "First"; String b = "Second"; String x = new StringBuffer(a).append(b).append(i).toString(); } long spent = System.currentTimeMillis() - start; timeStringBufferAppend2 += spent; System.out.println("StringBuilderAppend2 spent " + spent + "ms"); } public static void StringAddx() { String orignal = "Original"; orignal += "What"; } public void run() { int n = 1000 * 1000; for (int i = 0; i < 5; i++) { directStringAdd(n); stringBuilderAppend(n); stringBuilderAppend2(n); stringBufferAppend2(n); stringBufferAppend(n); stringBufferAppend(n); stringBufferAppend2(n); stringBuilderAppend2(n); stringBuilderAppend(n); directStringAdd(n); stringBuilderAppend(n); stringBuilderAppend2(n); stringBufferAppend2(n); stringBufferAppend(n); directStringAdd(n); } } }
And the test result is like
DirectStringAdd spent 67991 ms StringBuilderAppend spent 76464 ms StringBufferAppend spent 120135 ms StringBuilderAppend2 spent 65543 ms StringBufferAppend2 spent 118215 ms
I picked up the binary code snippet from the test case and list it in following table.
1: String x = a + b + i; 17 new java.lang.StringBuilder [56] 20 dup 21 aload 4 [a] 23 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [106] 26 invokespecial java.lang.StringBuilder(java.lang.String) [60] 2: String x = new StringBuilder().append(a).append(b).append(i).toString(); 17 new java.lang.StringBuilder [56] 20 dup 21 invokespecial java.lang.StringBuilder() [125] 24 aload 4 [a] 26 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [69] 3: String x = new StringBuilder(a).append(b).append(i).toString(); 17 new java.lang.StringBuilder [56] 20 dup 21 aload 4 [a] 23 invokespecial java.lang.StringBuilder(java.lang.String) [60]
1: String.valueOf(String) only cost a little. It only need return the reference.
2: append need a string copy. It cost quite much.
3: it even don't need valueOf. So it is the fastest.