25
\$\begingroup\$

Given that it is better to reuse objects than create new ones when developing with Android, is it worth while deleting the contents of a StringBuilder and reusing it?

StringBuilder b = new StringBuilder();
//build up a string
b.delete(0, b.length());
//reuse it

Compared to:

StringBuilder b1 = new StringBuilder();
//build up a string
StringBuilder b2 = new StringBuilder();
//create a new one
\$\endgroup\$
  • 1
    \$\begingroup\$ Is this even worth thinking about as long as it is not in a very tight loop? \$\endgroup\$ – Bobby Jan 8 '12 at 14:23
  • \$\begingroup\$ @Bobby - I have about a dozen separate strings of different sizes I need to build up in the one class, some in a loop, some not. Was not sure if it was worth while so that's why I am asking :) \$\endgroup\$ – Christopher Jan 10 '12 at 9:22
  • 1
    \$\begingroup\$ No, I meant this smells a lot like premature optimization. If you start worrying about "a dozen strings which need to be concatenated" without any profiling, then you've a knot in your brain. And I didn't mean that as an insult but as a heart-warm warning...been there, done that. Rule of thumb: If you concatenate strings in a loop, use a StringBuilder. Second rule of thumb: If you worry about speed, profile first. \$\endgroup\$ – Bobby Jan 10 '12 at 9:59
  • \$\begingroup\$ No insult taken and point noted. Truthfully, I am not overly concerned about optimization and I am an inexperienced programmer (or could you tell). I simply wanted to know if this would improved performance in some small way. Given that there is no .clear() method for StringBuilders, I half expected some one to say that iterating through the buffer to wipes it's contents is actually more expensive than garbage collecting. Never ask, never know. \$\endgroup\$ – Christopher Jan 10 '12 at 10:57
  • \$\begingroup\$ That's absolutely true. I think the difference between the two are not only small, but hardly noticeable. I just wanted to make sure that you understand that this question is hardly about real-world optimization \$\endgroup\$ – Bobby Jan 10 '12 at 11:17
24
\$\begingroup\$

Reusing doesn't need memory allocation, so it could be faster as @Mike Nakis mentioned. On the other hand it leads to ugly code: you use the same variable for two (or more) different purposes which could confuse readers, make maintenance harder. Unless you have a good reason to do that avoid it. I mean, you have a good reason if a profiling shows that creating new StringBuilder instances is a bottleneck in the application.

If you really have to reuse StringBuilders, try accessing them with different names with a helper method:

public static StringBuilder reuseForBetterPerformance(final StringBuilder sb) {
    sb.delete(0, sb.length());
    return sb;
}

Client code:

final StringBuilder b = new StringBuilder();
//build up a string
final StringBuilder c = reuseForBetterPerformance(b);
//reuse it
c.append(...)

It makes the code a little bit more readable, since the method name says why you're reusing the StringBuilder.

\$\endgroup\$
  • 2
    \$\begingroup\$ I don't understand the purpose of final \$\endgroup\$ – Xavier Combelle Jan 8 '12 at 14:31
  • \$\begingroup\$ If it wasn't made final then you could create a new one return it, but we want to return the same object but emptied. \$\endgroup\$ – luketorjussen Jan 8 '12 at 16:14
  • 3
    \$\begingroup\$ final helps readers, because they know that the reference always points to the same instance and it doesn't change later. programmers.stackexchange.com/questions/115690/… but you can find other questions on Programmers.SE in the topic. \$\endgroup\$ – palacsint Jan 8 '12 at 17:25
  • 1
    \$\begingroup\$ That helper function is an excellent compromise. Thanks for the idea. \$\endgroup\$ – Christopher Jan 10 '12 at 9:22
14
\$\begingroup\$

To add some hard numbers, I wrote a simple test application:

public static void main(String[] args) {
    int iterations = 1000000;
    int secondIterations = 25;

    long start = System.currentTimeMillis();
    for (int count = 0; count < iterations; count++) {
        StringBuilder builder = new StringBuilder();
        for (int secondCounter = 0; secondCounter < secondIterations; secondCounter++) {
            builder.append("aaassssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
        }
    }
    System.out.println("Recreation took: " + Long.toString(System.currentTimeMillis() - start) + "ms");

    start = System.currentTimeMillis();
    StringBuilder builder = new StringBuilder();
    for (int count = 0; count < iterations; count++) {
        builder.delete(0, builder.length());
        for (int secondCounter = 0; secondCounter < secondIterations; secondCounter++) {
            builder.append("aaassssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
        }
    }
    System.out.println("Reuse took: " + Long.toString(System.currentTimeMillis() - start) + "ms");
}

This gives the following output:

Recreation took: 10594ms
Reuse took: 1937ms

So, yes, as it seems reusing is indeed faster, at least if you test it in a tight loop with a small memory footprint. But please keep in mind that those numbers were generated by using 1 million iterations.

Keep two rules in mind:

  • First go for readability and maintainability.
  • Second...run a profiler.

This difference I see here is nothing I'd worry about until I really run into performance problems.

\$\endgroup\$
  • \$\begingroup\$ Wow, that's 5 times faster. Though I would be willing to bet that most of this difference is due to the fact that your "recreation" loop generates 2.2 gigabytes of garbage, so exercises the garbage collector, (to its detriment,) while the "reuse" loop produces a lot less garbage, close to nothing. \$\endgroup\$ – Mike Nakis Sep 23 '15 at 18:23
12
\$\begingroup\$

Yes, of course it is worthwhile, even in the great big world outside of Android.

According to the documentation about StringBuilder:

Every string builder has a capacity. As long as the length of the character sequence contained in the string builder does not exceed the capacity, it is not necessary to allocate a new internal buffer. If the internal buffer overflows, it is automatically made larger.

This means that deletion of the StringBuilder's contents will result in no memory allocation operation for its internal array of chars (it will just set its length to zero and return, maintaining its last capacity). Plus, you save the StringBuilder object itself.

\$\endgroup\$
  • \$\begingroup\$ thanks ur reply is very helpful for me ,so here i have some question that 1. what is the default capacity of string builder. 2. so what if StringBuilder dynamically resize its buffer that time it will allocate a new memory please confirm. \$\endgroup\$ – Utsav Jan 16 '15 at 6:45
  • \$\begingroup\$ The default initial capacity is 16, but you can set it to whatever you want when invoking the StringBuilder's constructor. Saying that the StringBuilder will dynamically resize its buffer is equivalent to saying that the StringBuilder will allocate new memory. \$\endgroup\$ – Mike Nakis Jan 17 '15 at 8:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.