33

May be I am splitting hair, but I was wondering in the following case:

String newString = a + b + c;  //case 1


String newString = a.concat(b).concat(c);   //case 2

StringBuilder newString = new StringBuilder(); //case 3
newString.append(a);
newString.append(b);    
newString.append(c);

Which is the best to use?

Best I mean in any way.

Reading about these, other posts say that the case 3 is not that optimal performance wise, others that case 1 will end up in case 3 etc.

To be more specific.

E.g., setting all aside, which style is more suitable to see it from another programmer if you had to maintain his code?

Or which would you consider as more programming efficient?
Or you would think is faster etc.

I don't know how else to express this.

An answer like e.g. case 3 can be faster but the vast majority of programmers prefer case 1 because it is most readable is also accepted if it is somehow well elaborated

Cratylus
  • 52,998
  • 69
  • 209
  • 339

6 Answers6

29

Case 1 is concise, expresses the intent clearly, and is equivalent to case 3.

Case 2 is less efficient, and also less readable.

Case 3 is nearly as efficient as case 1, but longer, and less readable.

Using case 3 is only better to use when you have to concatenate in a loop. Otherwise, the compiler compiles case 1 to case 3 (except it constructs the StringBuilder with new StringBuilder(a)), which makes it even more efficient than your case 3).

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 2
    +1: Case 2 creates one temporary object, just like the other cases, so its not clear to me its less efficient, but agree its the least likely to be clear. (Partly because its the least common to be used) – Peter Lawrey Jan 22 '12 at 17:52
  • It has 1 temporary object (well 2 in fact, if you count the char array) because there are just 3 strings to concatenate. If you add more strings, you'll have one more temporary object per string. – JB Nizet Jan 22 '12 at 17:58
  • 1
    Agreed it doesn't scale as well. For `a.concat(b)` its actually slightly better. – Peter Lawrey Jan 22 '12 at 18:05
  • It's also slightly better for `a.concat(b).concat(c)`, but that way lies madness. – Tom Hawtin - tackline Jan 22 '12 at 20:58
  • @JBNizet:How do you know these details?By reading the source code?Or you have other reference.I am asking because I want to learn these details too – Cratylus Jan 22 '12 at 21:11
  • 3
    First of all, it's been more than 14 years I program in Java, read technical articles, participate in Java newsgroups, etc. You learn lots of things in such a long period. Second, this question is a classic one. Third, the source code is indeed available. And fourth, using `javap -c Test.class` allows discovering how the compiler compiles `s = a + b + c`. – JB Nizet Jan 22 '12 at 21:18
  • 2
    The compiler does not compile case 1 to case 3, but the JVM translate case 1 to case 3 at runtime. – Amir Pashazadeh Jan 22 '12 at 23:20
  • 1
    @AmirPashazadeh: no, the compiler does it. Execute `javap -c Test.class`, where Test.class isa class containing these instructions, and you'll see. – JB Nizet Jan 23 '12 at 07:25
  • Assuming each sample program contains the same initalization of String a, b, and c...(String a = "Hello"; String b = "World"; String c = "!";)... Case 2 results in the *least* amount of JVM instructions at 28 instructions, case 1 results in 41 instructions, and case 3 results in 47 instructions. It seems to me, Case 2 would become the best performing if you are to concat a great many number of strings. – SnakeDoc Aug 08 '13 at 17:04
7

Case 3 is the most performance form, but the JVM converts case 1 to case 3.

But I believe case 2 is the worst, it is not as readable as case 1 and not as perform as case 3.

If you want to concat string in a loop just use case 3, you can test the performance gain easily, but if it is not in a loop (or you are not appending lots of strings in a sequence) they are almost the same.

The cases which you shall not use the + operator are:

String a = "";
for (String x : theStrings) {
    a += x;
}

or

String a = b + c;
a = a + d;
a = a + e;
a = a + f;
Amir Pashazadeh
  • 7,170
  • 3
  • 39
  • 69
2

As an addition to the aforementioned, there was a significant performance improvement done under JEP 280 for string concatenation only.

Pls refer to https://openjdk.java.net/jeps/280 and explanation https://dzone.com/articles/jdk-9jep-280-string-concatenations-will-never-be-t

In short, it means that from Java 9 "Hello " + "world" string concatenation is a preferred way even taking performance into account.

fyrkov
  • 2,245
  • 16
  • 41
2

Case 3 is better in most aspects. In case 3, you don't endup creating 3 string objects. Because of string immutability, 2 will have overhead of creating string object for each + (or) concat.

EDIT: Re-read the document and agree with most of comments, case 1 is case3.

kosa
  • 65,990
  • 13
  • 130
  • 167
  • And usually when you are doing text manipulation where performance matters, you are working with (a) a much larger volume of operations; and (b) more complex operations such as searching & inserting or deleting text. – user268396 Jan 22 '12 at 16:20
  • 2
    Modern JVMs convert case 1 to case 3 on runtime, so there won't be 3 String objects. But if there are lots of sequential concatenations (in different statements) or in a loop, then yes each statement creates a new immutable string. – Amir Pashazadeh Jan 22 '12 at 16:24
  • Consider two scenarios: 'String j = "Java"; j += "Rocks";' String j = "Java"; j = j.concat("Rocks"); At run-time "Java" and "Rocks" will be stored in a dedicated data structure, and these literals will be replaced in the source code by the reference to the appropriate entries in that data structure. Actually the string pool is populated when the classes are loaded and not when the code containing the string literals is run. It means each of the snippets will create only concatenated result. So IMO none of the option will create 3 String objects. – akhil_mittal May 02 '15 at 02:30
1

For simple cases, using + is clearly more readable.

For rare more complicated cases, StringBuilder makes sense. If you are splitting across lines, then + can quickly be a performance drag. Loops go from O(n) performance to O(n^2) - not a problem if n is small, but a big problem if n can be large in awkward circumstances (perhaps when doing something critical, or when someone is being malicious).

Contrary to the other answers, if you've only got three strings, concat may be the performance winner. Am I mental? No. Consider the other two options. You are creating a StringBuilder object, with an array. Appending the strings may well require the array to be replaced and you may have odd bits left at the end of the array (thefre's a certain granularity to allocation, so a few extra chars may or may not increase memory usage). You can compute the necessary capacity if you hate everyone that reads your code. Best case, two arrays (one for StringBuilder, one for the String (no sharing for the last eight years)) each of the size of the result text and two other objects (StringBuilder and String). Now for the concat you are going to allocate to String objects with two arrays, but the first array will be c.length() shorter. It's a win! Well, not for readability.

Disclaimer: JVM may do wild optimisation, including stack allocation after escape analysis and special casing of core classes. They are more likely to optimise common and simple code than hand-obfuscated code.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

On performance Analysis with JDK 1.8, I found case 2 as the winner in terms of performance.

class Solution {
public static void main(String[] args) {
   String s ="ABC", s1 = "", s2 = "", s3 = "";
   long t1 = System.nanoTime();
   for(int i = 1; i < 11; i++) {
       s1 = s1 + s;
   }
   System.out.println(System.nanoTime() - t1);
   System.out.println(s1);

   long t2 = System.nanoTime();
   for(int i = 1; i < 11; i++) {
       s2 = s2.concat(s);
   }
   System.out.println(System.nanoTime() - t2);
   System.out.println(s2);

   long t3 = System.nanoTime();
   StringBuilder sb = new StringBuilder();
   for(int i = 1; i < 11; i++) {
       sb.append(s);
   }
   s3 = sb.toString();
   System.out.println(System.nanoTime() - t3);
   System.out.println(s3);
 }
}

Results :

40615

ABCABCABCABCABCABCABCABCABCABC

9157

ABCABCABCABCABCABCABCABCABCABC

20243

ABCABCABCABCABCABCABCABCABCABC

  • 1
    please have a look at [JMH](https://openjdk.java.net/projects/code-tools/jmh/) and [Why Are Java Microbenchmarks Hard?](http://tutorials.jenkov.com/java-performance/jmh.html#why-are-java-microbenchmarks-hard) – user85421 Mar 09 '20 at 07:31
  • String interning by JVM is messing with your results – Akki Apr 28 '20 at 06:46
  • @user85421 very interesting link. I arrive to the same conclusion after seeing unpredictable results in junit (once a method wins and the next run it's another method which is faster). I would give a try to this JMH next time I'll need to test algo performance. – рüффп Jul 02 '20 at 12:02