Optimize String operationsTag(s): String/Number

(thanks to B. Wilkinson)
String concatenation via the "+" operator is one of the most convenient things to do in Java. It is also one of the most expensive, in terms of memory and performance.

When the compiler sees

String s = "abc" + someint + somearray[index];
or any other concatenation, it **ACTUALLY** generates (for runtime use) the code sequence that follows (or, at least, the bytecode equivalent of it):
StringBuffer temp = new StringBuffer( );
temp.append( String.valueOf( "abc" ) );
temp.append( String.valueOf( someInt ) );
temp.append( String.valueOf( someArray[index] );
String s = temp.toString( );
The weak spot in all this is the construction of the StringBuffer object: the size of the buffer is ALWAYS 16 characters. Then, as data is appended to the buffer, if more space is needed the size of the buffer is doubled and the old data is copied to the new buffer.

So to optimize we have to bypass the automatic StringBuffer when possible. In this JAVA How-to, a snippet is given to replace a character at a specific position. An optimized version, using the StringBuffer would be:

public static String replaceCharAt(String s, int pos, char c) {
     StringBuffer buf = new StringBuffer( s );
     buf.setCharAt( pos, c );
     return buf.toString( );
Only one buffer created, exactly the right size. Converting a StringBuffer to a String costs almost nothing, as the actual buffer is shared between the two.

In the same How-to, a snippet about removing a character in a String can be optimized like:

public static String removeChar(String s, char c) {
       StringBuffer r = new StringBuffer( s.length() );
       r.setLength( s.length() );
       int current = 0;
       for (int i = 0; i < s.length(); i ++) {
           char cur = s.charAt(i);
           if (cur != c) r.setCharAt( current++, cur );
       return r.toString();
In the original version, a new String object was created and discarded immediately!

The weak spot of the original method to remove a character is when the parameter s passed havee than 17 characters, the temporary StringBuffer created by the compiler will have to be extended. To optimize, simply rewrite the method using a StringBuffer with the correct size:

public static String removeCharAt(String s, int pos) {
       StringBuffer buf = new StringBuffer( s.length() - 1 );
       buf.append( s.substring(0,pos) ).append( s.substring(pos+1) );
       return buf.toString();
Check this nice article about String/StringBuffer optimization.
StringBuilder (JDK1.5)
The StringBuilder class, introduced in J2SE 5.0, differs from StringBuffer in that it is unsynchronized. When only a single thread at a time will access the object (the most common case), using a StringBuilder is more efficient than using a StringBuffer.

The code

StringBuffer sb = new StringBuffer(300);
for (int i = 0; i < 100; i++) {
is less efficient (in single-thread context) than
StringBuilder sb = new StringBuilder(300);
for (int i = 0; i < 100; i++) {

blog comments powered by Disqus