4 ways to concatenate Strings in Java - Best Performance

When we think about String Concatenation in Java, what comes to our mind is the + operator, one of the easiest way to join two String, or a String and a numeric in Java. Since Java doesn't support operator overloading, it's pretty special for String to have behavior. But in truth, it is the worst way of concatenating String in Java. When you concatenate two String using + operator e.g. "" + 101, one of the popular ways to convert int to String, compiler internally translates that to StringBuilder append call, which results in the allocation of temporary objects. You can see the real difference in performance of our example program, in which we have concatenated 100,000 String using + operator. Anyway, this article is not just about + operator but also about other ways to concatenate multiple Strings. There are four ways to do this, apart from the + operator, we can use StringBuffer, StringBuilder, and concat() method from java.lang.String class for the same purpose.

Both StringBuilder and StringBuffer classes are there for just this reason, and you can see that in our performance comparison. StringBuilder is winner and fastest ways to concatenate Strings. StringBuffer is close second, because of synchronized method and rest of them are just 1000 times slower than them. Here we will see example of all four ways of concatenating Strings in Java.




1) String Concatenation using + Operator

Easiest way of joining multiple String and numeric values in Java. Just remember that, when you have two or more primitive type values e.g. char, short or int, in the beginning of your string concatenation, you need to explicitly convert first of them to a String. For example System.out.println(200 + 'B' + ""); will not print 200B, instead it will print 266, by taking ASCII value of 'B' and adding them to 200.

On the other hand System.out.println("" + 200 + 'B') will print 200B. Unfortunately, this is the worst way to concatenate String in Java. Let's take a look at how String concatenation operator works in Java. When we write :

String temp = "" + 200 + 'B';

it's translated into

new StringBuilder().append( "" ).append( 200 ).append('B').toString();

All + operator are translated into several StringBuilder.append() calls before final toString() call.

Since StringBuilder(String) constructor allocates a buffer with only 16 char, appending more than 16 characters will require buffer reallocation. At last, StringBuffer.toString() calls create a new String object with a copy of StringBuilder buffer.

This means, to concatenate two String, you will need to allocate, one StringBuilder, one char array[16], one String object and another char[] of appropriate size to fit your input value. Imagine if you are doing this thousands times in your application, it's not only slow but also increase work load of Garbage Collector.

You can also see Java Performance The Definitive Guide By Scott Oaks to learn more about how to do performance testing in Java and how to tune different things in Java ecosystem to get the best performance from your Java application.

Concatenating String in Java


2) Using concat() method from java.lang.String

Concat(String str) method concatenates the specified String to the end of this string. I have hardly seen this used for concatenation, though. Inside, it allocates a char[] of length equal to combined length of two String, copies String data into it and creates a new String object using private String constructor, which doesn't make a copy of input char[], as shown below.

 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);

    }

  /*
    * Package private constructor which shares value array for speed.
    * this constructor is always expected to be called with share==true.
    * a separate constructor is needed because we already have a public
    * String(char[]) constructor that makes a copy of the given char[].
    */

    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;

    }

The performance of concat() method is similar to + operator and I don't recommend to use it for any production purpose.


3) Using StringBuffer

This was the proper way to concatenate multiple String, integer, and others prior to Java 5 when StringBuilder was not available. It's much faster than + operator and concert() method. The only drawback of this class is that all it's append methods are synchronized. Since we hardly share temporary string in a concurrent environment, the price for thread-safety is not desired in many cases, that's where StringBuilder is used. It works similar to StringBuilder synchronization. It also provides several overloaded append() method to concatenate integer, char, short etc.

How to concatenate multiple String in Java

4) Using StringBuilder

This is the best way to concatenate String in Java, especially when you are concatenating multiple Strings. It's not thread-safe, but you hardly need that during String concatenation. In next section we will see performance comparison of all these 4 ways to concatenate String in Java.


Performance comparison + Operator vs Concat vs StringBuffer vs StringBuilder

here is a sample Java program to find out which method gives us best performance for String concatenation.

public class Demo{

    public static void main(String args[]) throws IOException {
        final int ITERATION = 100_000;
        String s = "";

        // String Concatenation using + operator
        long startTime = System.nanoTime();
        for (int i = 0; i < ITERATION; i++) {
            s = s + Integer.toString(i);
        }
        long duration = (System.nanoTime() - startTime) / 1000;
        System.out.println("Time taken to concatenate 100000 Strings using + operator (in micro) : " + duration);

        // Using String concat() method
        startTime = System.nanoTime();
        for (int i = 0; i < ITERATION; i++) {
            s.concat(Integer.toString(i));

        }
        duration = (System.nanoTime() - startTime) / 1000;
        System.out.println("Time taken to concatenate 100000 Strings using concat method (in micro) : " + duration);
        
    // StringBuffer example to concate String in Java
        StringBuffer buffer = new StringBuffer(); // default size 16
        startTime = System.nanoTime();
        for (int i = 0; i < ITERATION; i++) {
            buffer.append(i);
        }

        duration = (System.nanoTime() - startTime) / 1000;
        System.out.println("Time taken to concatenate 100000 Strings using StringBuffer (in micro) : " + duration);

        // StringBuilder example to concate two String in Java
        StringBuilder builder = new StringBuilder(); //default size for worst case
        startTime = System.nanoTime();
        for (int i = 0; i < ITERATION; i++) {
            builder.append(i);
        }
        duration = (System.nanoTime() - startTime) / 1000;
        System.out.println("Time taken to concatenate 100000 Strings using StringBuilder append in micro) : " + duration);
    }
}

Output:
Time taken to concatenate 100000 Strings using + operator (in micro) : 29178349
Time taken to concatenate 100000 Strings using concat method (in micro) : 21319431
Time taken to concatenate 100000 Strings using StringBuffer (in micro) : 12557
Time taken to concatenate 100000 Strings using StringBuilder append (in micro) : 10641

You can clearly see that given everything same, StringBuilder outperform all others. It's almost 3000 times faster than + operator. concat() method is better than + operator but still very bad compared to StringBuffer and StringBuilder. StringBuffer took more time than StringBuilder because of synchronized method. I know that, you hardly join 100K Strings but even for small numbers it adds. if you run program to join just 10 Strings, you will notice significant difference in time spent Here is what I got when I ran this program for 10 iteration only :

+ operator took (in micros) : 177
concat() method took (in micros) : 28
StringBuffer took (in micros) : 21
StringBuilder append took (in micros) : 8

Though using + operator along with empty String is most easiest and obvious way to concatenate multiple String and numeric in Java, you should not use that, especially inside loops. Always use StringBuilder for string concatenation, the class is there for a reason, and if you are still using StringBuffer then get rid of that, because most of the time you don't really need thread-safety overhead of StringBuffer.  Unfortunately concat() method is only good when you need to concatenate exactly 2 strings. In short, always use StringBuilder to concatenate Strings in Java.

10 comments:

  1. From Java >1.6 plus operator is changed into StringBuilder by compiler. So what is the point in making code less readable by StringBuilder?

    ReplyDelete
    Replies
    1. @Michal, there are cases where using + operator is not optimal e.g. concatenating in loop, because Java will internally create and destroy StringBuilder instances. Sometime its better to have one StringBuilder object outside loop and just append on it while iterating over loop, instead of using + operator.

      Delete
    2. Yes there are cases, but these will make about 0.000000000001% of cases; making the explicit use of stringbuilder just a waste of time (and an unneeded premature optimization)

      Delete
    3. It depends on one's preferences and/or string you are creating but using StringBuilder might be far more readable than + operator.

      Delete
  2. I ran your code and got similar results. However, when I added a couple more cases, I was surprised:

    Case 1 - building a new string using '+'. (s = "one" + 2 + 3.0;)
    Case 2 - build a new string using StringBuilder (s = new StringBuilder().append("one").append(2).append(3.0).toString();)

    These results were MUCH different from your examples of *appending* to an existing string. In fact, using the '+' method was 100 times faster!

    ReplyDelete
  3. Hi, I added the String.join method like this:
    startTime = System.nanoTime();
    for (int i = 0; i < ITERATION; i++) {
    s = String.join(" ", Integer.toString(i));
    }
    duration = (System.nanoTime() - startTime) / 1000;
    System.out
    .println("Time taken to concatenate 100000 Strings using String.join in micro) : " + duration);

    it was much faster than concat and closer to the StringBuilder

    ReplyDelete
  4. final int ITERATION = 100_0;
    its giving strange result. Making StringBuffer fastest ?

    Time taken to concatenate 100000 Strings using + operator (in micro) : 9053
    Time taken to concatenate 100000 Strings using concat method (in micro) : 3796
    Time taken to concatenate 100000 Strings using StringBuffer (in micro) : 469
    Time taken to concatenate 100000 Strings using StringBuilder append in micro) : 485

    ReplyDelete
  5. Hello Shashank, which version of JDK you are using? StringBuffer is not really a problem in single threaded environment because uncontented lock acquisition has become faster with latest JVM and JIT version.

    ReplyDelete
  6. Any analysis on String.format?

    ReplyDelete