String
were replaced with StringBuffer
. The poster also reported that an application that previously ran for over 30 minutes was running in fewer than 7 seconds with a similar String
-to-StringBuffer
change.I have been cognizant of the difference between
String
and StringBuffer
for some time, but it is easy to forget how significant of an impact the incorrect use of the immutable String
can have until you use it or see it used incorrectly. I decided to play with String
, StringBuffer
, and StringBuilder
to see how dramatic the difference really is.The following code listing shows a simple Java class that pits
String
, StringBuffer
, and StringBuilder
in a contest that an immutable String
really has no business being in.
import java.util.ArrayList;
import java.util.List;
/**
* Simple class testing the rough differences in performance of String,
* StringBuffer, and StringBuilder in repeated (looping) String concatenations.
*
* @see java.lang.String
* @see java.lang.StringBuffer
* @see java.lang.StringBuilder
*/
public class StringPerformanceTester
{
final private String CONSTANT_STRING;
/**
* Constructor accepting the String to be used for appending or concatenating
* to the main String. The ability to specify this String provides additional
* flexibility in the performance testing.
*/
public StringPerformanceTester(final String stringToBeUsed)
{
CONSTANT_STRING = stringToBeUsed;
}
/**
* Concatenate Strings repeatedly the specified number of times.
*
* @param numberOfStrings Number of times to concatenate Strings.
* @return Time in milliseconds required to concatenate Strings.
*/
public long concatenateStrings(final int numberOfStrings)
{
String masterString = "";
final long startTime = System.currentTimeMillis();
for ( int i=0; i < numberOfStrings; ++i )
{
masterString += CONSTANT_STRING;
}
final long endTime = System.currentTimeMillis();
return endTime - startTime;
}
/**
* Concatenate Strings repeatedly the specified number of times using
* StringBuffer.
*
* @param numberOfStrings Number of times to append Strings.
* @return Time in milliseconds required to append Strings.
*/
public long useStringBuffer(final int numberOfStrings)
{
final StringBuffer masterString =
new StringBuffer(CONSTANT_STRING.length()*numberOfStrings);
final long startTime = System.currentTimeMillis();
for ( int i=0; i < numberOfStrings; ++i )
{
masterString.append(CONSTANT_STRING);
}
final long endTime = System.currentTimeMillis();
return endTime - startTime;
}
/**
* Concatenate Strings repeatedly the specified number of times using
* StringBuilder.
*
* @param numberOfStrings Number of times to append Strings.
* @return Time in milliseconds required to append Strings.
*/
public long useStringBuilder(final int numberOfStrings)
{
final StringBuilder masterString =
new StringBuilder(CONSTANT_STRING.length()*numberOfStrings);
final long startTime = System.currentTimeMillis();
for ( int i=0; i < numberOfStrings; ++i )
{
masterString.append(CONSTANT_STRING);
}
final long endTime = System.currentTimeMillis();
return endTime - startTime;
}
/**
* Main method for executing timing tests of different methods of String
* concatenation.
*
* @param numberOfStringsForTest Number of strings to be concatenated or appended.
* @return Results of running all three types of tests against provided
* number of Strings.
*/
public TestResults runTimingTests(final int numberOfStringsForTest)
{
final TestResults testResults =
this.new TestResults(numberOfStringsForTest);
testResults.millisecondsString = concatenateStrings(numberOfStringsForTest);
testResults.millisecondsBuffer = useStringBuffer(numberOfStringsForTest);
testResults.millisecondsBuilder = useStringBuilder(numberOfStringsForTest);
return testResults;
}
/**
* Main executable for testing String performance.
*/
public static void main(final String[] arguments)
{
final StringPerformanceTester me = new StringPerformanceTester("constant");
final List<TestResults> testResults = new ArrayList<TestResults>();
testResults.add( me.runTimingTests(1000) );
testResults.add( me.runTimingTests(10000) );
testResults.add( me.runTimingTests(100000) );
System.out.println(testResults);
}
/**
* Class for easy storage and output of test results.
*/
private class TestResults
{
private int numberOfStrings;
private long millisecondsString;
private long millisecondsBuffer;
private long millisecondsBuilder;
/**
* Constructor accepting the number of String concatenations or appends.
*
* @param numberOfStrings Number of String concatenations or appends.
*/
public TestResults(final int numberOfStrings)
{
this.numberOfStrings = numberOfStrings;
}
/**
* Provide String representation of me.
*
* @return String representation of me.
*/
@Override
public String toString()
{
final StringBuffer sb = new StringBuffer();
sb.append("#: " + numberOfStrings);
sb.append("\tSTRING: " + millisecondsString);
sb.append("\tBUFFER: " + millisecondsBuffer);
sb.append("\tBUILDER: " + millisecondsBuilder + "\n");
return sb.toString();
}
}
}
When the test above is run, the results are dramatic. The largest test run involved concatenating the same String one hundred thousand times using
String
's +=
operator and the append
method of the StringBuilder
and StringBuffer
classes. The results are so striking that the String's
results for 100,000 concatenations is most easily measured in minutes while the StringBuffer
and StringBuffer
accomplish the same concatenation in time most easily measured in miliiseconds.The following screen snapshot shows the results of the run.
For performance tests and benchmark runs to be useful, many runs of the same code under carefully controlled circumstances are often required to get accurate results. However, in this simple case, the sheer magnitude of the performance difference implies that even variables introduced by too few of runs in too loosely controlled environments cannot be explained by these inconsistencies.
There are several things one could try in conjunction with these tests for further measurements. For example, I took advantage of knowing the size of my
StringBuffer
and StringBuilder
at instantiation time. If I started with smaller sizes and forced them to expand as needed, I might see a little worse performance. I could also try running with the -server
option and could try different sizes of Strings and different number of Strings.One lesson I think it pretty clear from this is that when frequent String concatenation is required (especially in loops),
StringBuffer
is easily preferable to String
. I'll generally favor StringBuffer
in such situations and only use the unsynchronized StringBuilder
if I need to squeeze every last millisecond out and am in a single-thread situation. Otherwise, the synchronized StringBuffer
competes well enough with the unsynchronized StringBuilder
that I prefer StringBuffer
for general String concatenation needs.There are several good resources that explain the difference between
String
, StringBuffer
, and StringBuilder
. A concise overview of the differences and when to use each is provided in Java Tip - Difference Between String, StringBuffer, and StringBuilder. A useful blog comparing these is Difference Between String and StringBuffer/StringBuilder in Java. A satirical look at String
versus StringBuffer
is available in Maximize Java Performance by Avoiding StringBuffer (some of its feedback comments are interesting as well). Another worthwhile blog entry is StringBuffer/StringBuilder Performance Improvements.UPDATE (18 August 2008): Another interesting resource on Strings is the PMD (PMD doesn't stand for anything) String and StringBuffer Rules description.
UPDATE (25 August 2008): See the blog entry Don't Use StringBuffer! for a compelling post on why StringBuilder is almost always preferable to StringBuffer.
UPDATE (15 January 2010): See the blog post About String Concatenation in Java or 'don’t fear the +' for additional focus on the differentiation between a single statement and multiple statements using + operator.
1 comment:
String is an immutable class while StringBuilder & StringBuffer are mutable classes. StringBuffer is synchronized while StringBuilder is not.
Below link can be useful to find more differences between String, StringBuffer & StringBuilder
Difference between String, StringBuffer and StringBuilder
http://newtechnobuzzz.blogspot.com/2014/07/difference-between-string-stringbuffer.html
Post a Comment