2

I have made a performance test

    String test="";
    Date st = new Date();
    logger.info("start "+(new Date()).toString());
    for(int i = 0;i<100000;i++) {
        test += "test";  
    }
    logger.info("end "+(new Date()).toString());

    test="";
    StringBuilder sb = new StringBuilder(test);
    logger.info("start Sb "+(new Date()).toString());
    for(int i = 0;i<100000;i++) {
        sb.append("test");  
    }
    test = sb.toString();
    logger.info("end sb "+(new Date()).toString());

and the result is

start Fri Jun 30 10:34:42 KRAT 2017
end Fri Jun 30 10:34:55 KRAT 2017
start Sb Fri Jun 30 10:34:55 KRAT 2017
end sb Fri Jun 30 10:34:55 KRAT 2017

The difference is 13 seconds!!!

but everywhere I read slogans that "+" operator is more faster because since java 1.6 it was optimized. What's wrong? Where I am mistaken?

Cœur
  • 37,241
  • 25
  • 195
  • 267
John
  • 446
  • 6
  • 16
  • 3
    The `+=` approach is O(n^2), the `append` approach is O(n). The `+` approach is not optimized for loops. See https://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java (and in particular the notes there about when you should move to StringBuilder instead of `+`). – yshavit Jun 30 '17 at 04:11
  • 3
    Microbenchmarks are very tricky. You results don't prove a lot (although `+` will be slower in general) since + also uses `StringBuilder.append` internally. Your first loop will be interpreted by HotSpot, and since you have created a HotSpot in `StringBuilder.append`, it will be JIT-compiled, and your second loop will be much faster. You at least need to run your code few times to warm up the VM before doing the real performance test. – Erwin Bolwidt Jun 30 '17 at 04:14
  • Thanks! Several tests shows 7 seconds – John Jun 30 '17 at 04:43
  • See https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Ryan Leach Jun 30 '17 at 06:13

2 Answers2

5

The + is implemented by allocating a StringBuilder and calling append. The following lines of code are equivalent:

String s = new StringBuilder().append(string1).append(string2).append(string3).toString();
String s = string1 + string2 + string3;

Unfortunately the compiler isn't good at recognizing a chain of String concatenations in a loop, so the following snippets are also equivalent.

    String test="";
    for(int i = 0;i<100000;i++) {
        test += "test";  
    }

    String test="";
    for(int i = 0;i<100000;i++) {
        test = new StringBuilder().append(test).append("test").toString();  
    }

Note that the latter allocates a new StringBuilder in every loop iteration and calls toString on the result every time. You can verify this by looking at the bytecode generated by 'javac Test.java && javap -c Test'. As a rule of thumb, if your string concatenation can be written as one expression, prefer the + operator for readability.

Side Note about writing these benchmarks: The order in which you test each alternative affects their performance. You should probably write two separate main methods for each version so that your tests are independent. Also, you're doing a lot of work with a variable named test but not using it anywhere. The compiler is free to optimize out your first for loop since the value of test is reset immediately afterwards.

Evan Darke
  • 604
  • 4
  • 7
0

"+" operator on a normal String is much slower than "append" operation on StringBuilder. Asymptotically, "+" operator on a normal String has O(n*m) time complexity where n is the size of old string and m is the size of the string that is to be appended, whereas "append" operation on StringBuilder has just O(m) complexity.

Explaination:

String is immutable. So, every time you do test + "test", a new string is created and all the characters of test string are appended to the new string, followed by all the characters of "test". Thus, total characters that are appended is test.length() + "test".length().

StringBuilder is mutable. So, every time you do sb.append("test), only the characters of "test" are appended to the existing "sb". Thus, total characters that are appended is "test".length().

iavanish
  • 509
  • 3
  • 8