StringBuffer is dead, long live StringBuffer
Overview
StringBuilder was introduced seven years ago as a replacement for StringBuffer where you didn't need thread safety.From the Javadoc for StringBuilder
This class provides an API compatible with StringBuffer, but with no guarantee of synchronization. This class is designed for use as a drop-in replacement for StringBuffer in places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.
StringBuffer is dead?
So you might believe that StringBuffer is basically dead because it has very few uses which cannot be replaced by StringBuilder and those are neatly wrapped by classes like StringWriter. However, if the JDK is anything to go by, having a drop in replacement is just not enough to get people to migrate existing code.Class | Uses in the Java 6 update 25 src.zip |
---|---|
StringBuffer | 1,409 |
StringBuilder | 311 |
Sun/Oracle can't force other developers to migrate their code, but they could at least update their own code, with their own drop-in replacement, over a seven year period.
Some places StringBuffer is used as a local variable
These are methods which could be called many times.
- BufferedReader.readLine()
- RandomAccessFile.readLine()
- Double.toHexString(double)
- URLDecoder.decode() and URLEncoder.encode()
- DecimalFormat (internal private methods)
- Currency.getInstance(Locale)
- Proeprties (internal private method)
- SimpleFormatter, XMLFormatter.format(LogRecord)
- Constructor, Field, Method, Modifier
- ByteBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer.
- SimpleDateFormat.formatToCharacterIterator(Object)
- Attribute.write(OutputStream)
- Matcher, five methods
- Manifest.write(OutputStream)
Escape Analysis
Something which is supposed to make the question less important is Escape Analysis which promises to identify local variables and turn off synchronisation when it is not required. This promise may have stalled migration of StringBuffer to StringBuilder.So does it work?
public static void main(String... args) { String text = "A short piece of text for copying"; int runs = 1000000; for (int i = 0; i < 7; i++) { { long start = System.nanoTime(); StringBuffer sb = new StringBuffer(text); for (int r = 0; r < runs; r++) copyStringBuffer(sb); long time = System.nanoTime() - start; System.out.printf("StringBuffer took an average of %,d ns%n", time/runs); } { long start = System.nanoTime(); StringBuilder sb = new StringBuilder(text); for (int r = 0; r < runs; r++) copyStringBuilder(sb); long time = System.nanoTime() - start; System.out.printf("StringBuilder took an average of %,d ns%n", time/runs); } } } public static String copyStringBuffer(StringBuffer text) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < text.length(); i++) sb.append(text.charAt(i)); return sb.toString(); } public static String copyStringBuilder(StringBuilder text) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < text.length(); i++) sb.append(text.charAt(i)); return sb.toString(); }prints the following when the -XX:+DoEscapeAnalysis flag is used.
StringBuffer took an average of 1,723 ns StringBuilder took an average of 189 ns StringBuffer took an average of 1,709 ns StringBuilder took an average of 176 ns StringBuffer took an average of 1,708 ns StringBuilder took an average of 175 ns StringBuffer took an average of 323 ns StringBuilder took an average of 176 ns StringBuffer took an average of 324 ns StringBuilder took an average of 175 ns StringBuffer took an average of 324 ns StringBuilder took an average of 174 ns StringBuffer took an average of 327 ns StringBuilder took an average of 174 nsIn this example, the StringBuffer is almost as fast as StringBuilder if called enough, but it still takes almost twice as long. For this example at least, a simple change from StringBuffer to StringBuilder is the simplest way to improve performance.
Conclusion
When you have an improvement, you cannot assume that it will be adopted over time. A less passive and more deliberate may be what is required.Escape Analysis may one day avoid the need to switch from StringBuffer to StringBuilder, but in the meantime a simple change to the source may still be required.
Comments
Post a Comment