String concatenation has always been a well-discussed topic among Java developers. It’s been costly.
Let’s first dig into the issue of why it’s costly.
In Java, string objects are immutable, which means once it is created, you cannot change it. So when we concatenate one string with another, a new string is created, and the older one is marked for the garbage collector.
Let’s say we need to concatenate a million strings. Then, we are creating 1 million extra strings which will eventually be garbage collected.
In string concatenation, under the hood, several operations occur. Using the dot operator to concatenate string is equivalent to using the String#concat(String)
method.
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
As you can see, a new array of characters is created with a size of the length of the existing value plus the size of value that is appended. Then, their value is copied to the new array instance. Finally, a new String object is created from the array and returned.
So it’s a lot of operations, which ends up being O(n^2) complexity — if you really want to calculate this issue out.
To solve this problem, the StringBuilder class is used. It works like a mutable String object. The append method helps to avoid all the copying required in string concatenation. It has O(n) complexity, which is better than O(n^2).
However, Java does this string concatenation using StringBuilder by default for the simple cases..
From the Java specifications:
To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
Well, the Java compiler does that.
public class StringConcatenateDemo {
public static void main(String[] args) {
String str = "Hello ";
str += "world";
}
}
The above code will produce the following bytecode:
public class StringConcatenateDemo {
public StringConcatenateDemo();
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 Hello
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String world
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: return
}
As you can see in the bytecode, StringBuilder is used. So we don’t need to use StringBuilder anymore in Java.
However, This is true for the simple cases. If you need to concatenate inside the loop, it is always suggested to use StringBuilder.
TL; DR
For simple string concatenation, you need not use StringBuilder
, java compiler will do the trick for you. However, if you need to concatenate inside a loop, you need to manually apply StringBuilder.
For better understanding please go to this link.
Edit: The earlier I made this case with only Java 8. Turns out I was wrong. So I stand correct myself. It has nothing to do with java 8.