String Concatenation in Java

Overview

1. Introduction

In this article, we'll look at two ways to concatenate Strings in Java, the associated problems, and how to avoid them using StringBuilder and StringBuffer classes.

2. String Immutability

A String in Java is an immutable collection of characters. String Immutability means every time we concatenate a String with another, the JVM creates a new String with the result of the concatenation.

If not done correctly, String concatenation can cause problems like out-of-memory errors. We'll see examples of this scenario and how to avoid them in the following sections.

3. The + Operator and the concat Method

The straightforward ways to concatenate Strings are the + operator and the concat method. Let's try one example to illustrate both ways.

 1public class Concatenation {
 2
 3  public static void main(String[] args) {
 4    String name = "Ritchie";
 5    String lastName = " Blackmore";
 6
 7    String completeNameWithOperator = name + lastName;
 8    String completeNameWithConcat = name.concat(lastName);
 9
10    System.out.println("Complete name using the + sign operator: " + completeNameWithOperator);
11    System.out.println("Complete name using the concat method: " + completeNameWithConcat);
12  }
13}

The code above generates the following output:

1Complete name using the + sign operator: Ritchie Blackmore
2Complete name using the concat method: Ritchie Blackmore

Both ways achieve the same result and create a new String with the result of the concatenation.

3.1. Problems with + and concat

The problems associated with the + and concat appear when we modify a String inside a loop. Imagine we need to build a String that represents the natural order of numbers from 1 to 1000, each number separated by a comma. One way of doing that is looping from 1 to 1000 and concatenating the current loop counter followed by a comma. Let's check that out in the code:

 1public class LoopConcatenation {
 2
 3  public static void main(String[] args) {
 4
 5    String numberSequence = "";
 6
 7    for (int i = 0; i < 1000; i++) {
 8      numberSequence = numberSequence.concat(",").concat(String.valueOf(i));
 9    }
10
11    System.out.println(numberSequence);
12  }
13}

The above code runs normally and outputs all the numbers of the sequence. The problem is that the JVM creates two new Strings for each loop counter:

  1. One created by the concat(",") method call.
  2. Another created by the concat(String.valueOf(i)) method call.

The JVM creates two thousand Strings in this for loop. Manipulate Strings inside for loops using either +, or concat introduces a potential memory leak in the application. At scale, thinking about hundreds of thousands of concatenations, that application may crash due to out-of-memory errors.

4. The StringBuilder Class

Avoid the previously-mentioned problematic scenario by using one of the String builder classes. The StringBuilder and StringBuffer classes store a mutable array of chars where we can change its content without creating new Strings.

We manipulate that array using the append method to add more text and the toString method to transform it into a String. Let's use the code below to show how to use StringBuilder methods:

 1public class StringBuilderExample {
 2
 3  public static void main(String[] args) {
 4    StringBuilder builder = new StringBuilder();
 5
 6    for (int i = 0; i < 1000; i++) {
 7      builder.append(",").append(i);
 8    }
 9
10    System.out.println(builder.toString());
11  }
12}

Instead of using + or concat from the String class, we add new text using append. Differently from the previously mentioned methods, the append method modifies the internal char array of the StringBuilder object without creating a new String. In the end, we transform that char array into a String using the toString method.

The output is the same as the previous solution. However, in this case, the code is much more efficient regarding memory usage. Here, the JVM creates only one StringBuilder and one String.

4.1. StringBuilder vs StringBuffer

We used the StringBuilder class in the previous example to create the desired result. The StringBuffer class achieves the same effect with one nuance: StringBuilder doesn't implement thread safety.

The StringBuffer works the same as the StringBuilder class. Both have the same methods and serve the same purpose of concatenating Strings. The only difference is that StringBuffer is thread-safe. Let's have a look at the signature of the append method of them:

  • StringBuilder append:
1public StringBuilder append(Object obj)
  • StringBuffer append:
1public synchronized StringBuffer append(Object obj)

All methods in StringBuffer are synchronized. So, if two threads write in the same String at the same time, we can guarantee that StringBuffer will keep the numbers in order. Using StringBuilder in multi-threaded environments, we can't guarantee the order of the number sequence.

5. Conclusion

In this post, we've looked at the problems associated with the + and concat methods and how to avoid them using StringBuilder and the StringBuffer classes.