StringBuffer considered harmuful

That building Strings using StringBuffer (and in Java 5 StringBuilder) is much faster and less memory expensive than using String concatenation, is not a secret.
But porting this concept to extremes, you can fall in coding conventions that mandate to never use String concatenation. And you can face hibernate queries like this:

 1String alias = "cd";
 2
 3StringBuffer expression = new StringBuffer("select ");
 4expression.append(alias).append(".account.name, ");
 5expression.append(alias).append(".startDate, ");
 6expression.append("sum(").append(alias).append(".duration), ");
 7expression.append("sum(").append(alias).append(".cost), ");
 8expression.append(alias).append(".account.id from ");
 9expression.append(Test.class.getName()).append(" as ").append(alias);
10expression.append(" order by ").append(alias).append(".startDate desc ");
11
12String result = expression.toString();
13// hibernate.executeQuery(...result...);

Cool! Only real programmers can read it, isn’t that? ]:)
You can imagine, that I love to write code this way… Jokes apart, imagine the time wasted beeing caught executing wrong queries and debugging this!
I tried to discuss this rule, but no-way… being a contractor, I always have to surrender to major forces :-), and this costs me pains. But at least this time we’ve discussed (a little, and here’s my rant). So the code it’s there. Fortunately in Hibernate 3.x we use Criterion and Projection APIs, so wrinting this kind of code is nomore in my task list. Few!
But going back at that time, you may think that a code like this below is easier to read but slower to execute:

 1String expression =
 2    "select " +
 3        "cd.account.name, " +
 4        "cd.startDate, " +
 5        "sum(cd.duration), " +
 6        "sum(cd.cost), " +
 7        "cd.account.id " +
 8    "from " +
 9        Test.class.getName() +
10        " as cd " + "order by " +
11        "cd.startDate desc";
12String result = expression;
13// hibernate.executeQuery(...result...);

If you think that it’s slower, you are wrong! Compilers are there to optimize code. String concatenations is made using StringBuffers by the compiler. The compiler can also inline string constants in a single one. Here’s an example on how the compiler can arrange our previous code:

1String expression = (new StringBuffer("select cd.account.name, cd.startDate, sum(cd.duration), sum(cd.cost), cd.account.id from ")).append(Test.getName()).append(" as cd ").append("order by ").append("cd.startDate desc").toString();

I’ve made a little class to compare performance between StringBuffers and String Concatenations for methods building queries like this.
Here’s the code (results follow)

 1public class Test {
 2    public interface Repeatable {
 3        void iteration(long loopId);
 4    }
 5
 6    public static long repeat(int times, Repeatable repeatable) {
 7        long initial = System.currentTimeMillis();
 8        for (long c = 1; c <= times; c++)
 9            repeatable.iteration(c);
10        long elapsed = System.currentTimeMillis() - initial;
11        return elapsed;
12    }
13
14    private static Repeatable stringBufferTest = new Repeatable() {
15        public void iteration(long loopId) {
16            String alias = "cd";
17            StringBuffer expression = new StringBuffer("select ");
18            expression.append(alias).append(".account.name, ");
19            expression.append(alias).append(".startDate, ");
20            expression.append("sum(").append(alias).append(".duration), ");
21            expression.append("sum(").append(alias).append(".cost), ");
22            expression.append(alias).append(".account.id from ");
23            expression.append(Test.class.getName()).append(" as ").append(alias);
24            expression.append(" order by ").append(alias).append(".startDate desc ");
25            String result = expression.toString();
26        }
27    };
28
29    private static Repeatable stringConcatenationTest = new Repeatable() {
30        public void iteration(long loopId) {
31            String expression =
32                "select " +
33                    "cd.account.name, " +
34                    "cd.startDate, " +
35                    "sum(cd.duration), " +
36                    "sum(cd.cost), " +
37                    "cd.account.id " +
38                "from " +
39                    Test.class.getName() +
40                    " as cd " + "order by " +
41                    "cd.startDate desc";
42            String result = expression;
43        }
44    };
45
46    public static void main(String[] args) {
47        for (int loopCount = 1; loopCount <= 5; loopCount++) {
48            int times = 1000000;
49            long stringBuffElapsed, stringConcatElapsed;
50
51            stringBuffElapsed = repeat(times, stringBufferTest);
52            stringConcatElapsed = repeat(times, stringConcatenationTest);
53
54            System.out.println("Results for iteration " + loopCount);
55            System.out.println("StringBuffer Build. " + stringBuffElapsed);
56            System.out.println("String  Concatenat. " + stringConcatElapsed + "\n");
57        }
58    }
59}

Here the output:

Results for iteration 1
StringBuffer Build. 3085
String  Concatenat. 1492

Results for iteration 2
StringBuffer Build. 3044
String  Concatenat. 1612

Results for iteration 3
StringBuffer Build. 3205
String  Concatenat. 1793

Results for iteration 4
StringBuffer Build. 3745
String  Concatenat. 1783

Results for iteration 5
StringBuffer Build. 3505
String  Concatenat. 2353

For this case, String concatenations is twice faster than StringBuffer!

Conclusions: don’t use String concatenations to build big strings (expecially inside loops), use StringBuffers everytime possible, but leave your source code readable! And… never forget Rules of Optimization. Personally, I prefere code (a little) slower but more readable. And, when possible, faster and more readable, like in this case.


One Response to “StringBuffer considered harmuful”  

  1. 1 CC

    Hi,

    I understand what you are trying to say. BUT i can’t agree on some points and a liite bit confused of how/ why are you demonstrating such stuff … so prob., the questions I would ask here:

    Why are you creating a new StringBuffer each time?
    Without running this test, I can assume taht will be slow down the process, if you re-locate thet one in front of loop OR insert a real new String() on each place where you work with strings, that might be closer to a real compare.

    Even if they are internal compiler, whci is right an very important, we might provide at least similar stuff; for instance:

    String expression = new String();
    expression = “bla”;
    expression = “bla ba”;

    OR choose the same logic on StringBuffe side:

    String expression = new StringBuffer( “bla”.length*3 );
    expression.append(“bla”).append(bla”).append(bla”);

    Well … however, I won’t use Strings anyway if I can avoid it ;)
    If you think that’s better, of course that will be fine.

    Cheers!

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Current ye@r *


Calendar

June 2005
M T W T F S S
    Jul »
 12345
6789101112
13141516171819
20212223242526
27282930  

Follow me

twitter flickr LinkedIn feed

Subscribe by email

Enter your email address:

Archives


Categories

Tag Cloud


Listening