Note: this article is based on the Oracle compiler supplied with SDK's up to Java 7Concatenating Strings in LoopsThis Q&A article attempts to explain why it may be important to use StringBuilder objects when concatenating Strings within a loop.
Why use StringBuilder when we can concatenate Strings using the '+' operator?This is a common question and there are 2 answers to it:
So why the different answers?The key to understanding this is to understand how the Java compiler handles string concatenation. If you compile the following code:
|
| And then view the generated p code using the javap command you will see the following:
|
| This output may not make much sense to you but the javap command handly adds comments showing which methods are being called. Looking at the comments shows that for every iteration of the loop a new StringBuilder object is being created, 3 strings are appended to it and then the StringBuilder is converted back to a String.
But we're not using a StringBuilder we are using the concatenation operator?True but Java handles String concatenation using StringBuilder objects so everytime you use '+' with Strings a new StringBuilder object is created to do the work.
But why are there 3 calls to append() when we are only appending 2 strings?The first call to append() loads the current value of allScores into the empty StringBuilder and the following 2 calls to append() add the two strings to it. Finally the toString() method is called to extract the String and assign it back to the allScores variable.
So if the compiler is already using StringBuilder why do we need to explicitly use it?The generated code creates a new StringBuilder for each iteration of the loop but the problem can be solved using only one StringBuilder object which can be created outside of the loop and then repeatedly appended to. Therefore, we can write the previous example as follows:
|
| And looking at the generated p code you will see there is now only one StringBuilder object created, 2 calls to append() within the loop and one call to toString() after the loop:
|
| So the second approach is likely to be more efficient than the first example, but by how much? The answer to that is a little surprising. If we write a simple test program to time both approaches we can get some idea of the relative efficiency of each approach. For example:
|
| On my system this produces the following output, you may have to adjust the number of iterations to produce reasonable output values on your system.
|
| Yes, approach two really is approximately 74,000 times quicker in this example.
So why such a massive difference?In the first approach for every iteration of the loop a new StringBuilder object is created and all of the characters in the String have to be copied into the object. Then, once the additional text has been appended, a new String object is created and again all the characters have to be copied. Now when the String only holds a few characters this takes very little time but as the String grows in length copying of all the characters twice for each iteration becomes increasingly time consuming, not to mention the increased probability of garbage collection cycles. A good way to see how approach one becomes increasingly inefficient as the length of the String increases is to run the following code and see how much the rate of printing slows down once as the String length grows.
|
|
ConclusionIf you have performance issues with string concatenation and you are concatenating large strings and/or many strings, changing your code to explicitly use a StringBuilder object may well resolve your performance problems.
| |