Vitalii has posted 3 posts at DZone. You can read more from them at their website. View Full User Profile

A Better StringBuilder

01.19.2011
| 13658 views |
  • submit to reddit
Almost noone likes using string "+" for complex text creation. Long chains of .append with StringBuilder also look ugly. So, often one will see something like the following:
StringBuilder builder = new StringBuilder();
builder.append("Hello, world\n")
if (name != null) {
    builder.append(String.format("My name is %s\n", name));
} else {
    builder.append(String.format("Please email me at %s\n", email);
}
doUse(builder.toString();
But does anyone think that this code is almost as bad in performance as str += name? String.format call creates a new StringBuilder, new Formater, then creates String out of this thing and then appends String to your builder. Consider a better way:
Formatter formatter = new Formatter();
formatter.format("Hello, world\n");
if (name != null) {
    formatter.format("My name is %s\n", name);
} else {
    formatter.format("Please email me at %s\n", email);
}
doUse(formatter.toString();
This code has less boilerplate and creates less objects. The only thing you should watch for is if in formatter.format(str) str contains '%' character. But you can always use formatter.format("%s", str) or formatter.out().append(str).
2
Your rating: None Average: 2 (4 votes)
Published at DZone with permission of its author, Vitalii Tymchyshyn.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Andy Till replied on Wed, 2011/01/19 - 3:44am

I don't think this is much of an improvement in terms of readability.  In the original example the optimum way for performance would be to append all components of the string separately.  How about...

StringBuilder builder = new StringBuilder();

builder.append("Hello World\n", "My name is ", name, "\n");

If the StringBuilder object had a method that could take many arguments then it could actually look better and have better performance than individual appends since all of the stringlengths of the arguments could be calculated and the array resized which would avoid it being resized several times.

 

 

Thomas Nagel replied on Wed, 2011/01/19 - 3:46am

why not use the StringBuilder properly:

 

StringBuilder builder = new StringBuilder();

builder.append("Hello, world\n")

.append("My name is ").append(name).append("\n")doUse(builder.toString();

 

And just in case you didn't notice: Formatter is the bad guy, not StringBuilder.

 

regards

blue

Mladen Girazovski replied on Wed, 2011/01/19 - 4:37am

Whats wrong with appending Strings with +?

The compiler translates it into a StringBuilder, unless its used in a loop its perfectly fine and readable.

No offence intented, but it think this is a solution to a problem that does not exists, looks to me like people are confusing premature optimisation with good code ;)

Milos Silhanek replied on Wed, 2011/01/19 - 5:01am

Why the Formatter way " creates less objects" ? I create StringBuilder or the Formatter - its one. The Formatter has its inner buffer for string/char[] manipulation intuitively. If you have a look into Formatter source you see it uses StringBuilder - so you create two objects. I do not know sbuilder.append("one", "two");

Andy Till replied on Wed, 2011/01/19 - 5:27am in response to: Mladen Girazovski

How do you know this is premature?

Mladen Girazovski replied on Wed, 2011/01/19 - 6:25am in response to: Andy Till

How do you know this is premature? 

Simple:It is assumed that StringBuilder has a better performace than concating String with + with an example.

The example given:

StringBuilder builder = new StringBuilder();

builder.append("Hello, world\n")    .append(String.format("My name is %s\n", name));doUse(builder.toString();

 The assumption:

But does anyone think that this code is almost as bad in performance as str += name?

Then a very strange notation is shown to "fix" the verbose StringBuilder syntax...

The assumption is wrong,  at least with this example.

The compiler will convert a String concation with + to a StringBuilder, always.

The only case in which this will give you "worse performance" than using a StringBuilder directly is in a loop.

Apart from that, no profiling has been performed before or after the "optimisation", otherwise the lack of performace increasement would have shown in the first place.

So, code was made more complex for no reason at all, expect for theoreritcal improvements. In this case, the assumptions which lead to a useless theoretical improvement, where wrong.

The rule is simple:

Write simple, readable code, don't optimize unless you have to, check if you really improved performance with the change in a measurable range, otherwise drop the "improvement" and proceed to write simple, readable code.

Fabien Bergeret replied on Wed, 2011/01/19 - 8:25am

I've never seen any performance problem in an application related with String concatenation.

Bad SQL queries are generally the wrong guys.

Object transformation (POJO to DTO to POJO) in the different layers of the application.

Inefficient GUI Frameworks (parsing tons of Json data to generate tons of HTML).

Here are my 'usual suspects'. You may be right. You may be wrong. It won't change real world application performance...

Philippe Lhoste replied on Wed, 2011/01/19 - 9:42am

"Almost noone likes using string "+" for text creation"

Really? I still see it used a lot, and still use it a lot. Except in a loop, of course...

The (first) example you give is precisely a good case of bad usage of StringBuilder, using an heavy, unreadable notation to do what the compiler will do for you if you use +

StringBuilder is a good tool, but don't abuse it.

And as others said, avoid premature optimizations...

Vitalii Tymchyshyn replied on Wed, 2011/01/19 - 12:26pm in response to: Andy Till

But it has no such method. Even if it had, you could not format as Formatter can.

Vitalii Tymchyshyn replied on Wed, 2011/01/19 - 12:28pm in response to: Thomas Nagel

A lot of .append calls prevent good readablity. It's hard to read, it makes long lines that you need to wrap somewhere. In result it's hard to see what one will get in result

Vitalii Tymchyshyn replied on Wed, 2011/01/19 - 12:35pm in response to: Mladen Girazovski

OK, an example if too simplistic. I'd better add some "if" to it. Like
Formatter formatter = new Formatter();
formatter.format("Hello, world\n");
if (name != null) {
    formatter.format("My name is %s\n", name);
} else {
    formatter.format("Please email me at %s\n", email);
}
doUse(formatter.toString();
vs
StringBuilder builder = new StringBuilder();
builder.append("Hello, world\n")
if (name != null) {
    builder.append(String.format("My name is %s\n", name));
} else {
    builder.append(String.format("Please email me at %s\n", email);
}
doUse(builder.toString();

Vitalii Tymchyshyn replied on Wed, 2011/01/19 - 12:36pm in response to: Milos Silhanek

Because String.format creates new fresh formatter, that has inner buffer and then calls toString on formatter to make a String that will be added to "our" StringBuilder

Vitalii Tymchyshyn replied on Wed, 2011/01/19 - 12:38pm in response to: Fabien Bergeret

About performance, all one can tell without measurements: "It depends". :) But the point is that Formatter method is both faster and smaller/better look. If it has both gains and no losses, why one should not use it.

Vitalii Tymchyshyn replied on Wed, 2011/01/19 - 12:41pm in response to: Philippe Lhoste

Thanks for pointing out that my example was simplified too much. I did add a better one above. May be I will try to update the original text now.

Nicolas Bousquet replied on Wed, 2011/01/19 - 2:43pm

Ok so I benchmarked your two versions and a last one using + (and so no formater at all) :

With StringBuilder and String.format : 420000 executions / second.

With Formatter : 389000 executions / second.

With + operator : 2321000 executions / second.

Conclusion : the version you said as being the best (Using formater only) is the worse contender, being sligtly slower than using a String.format each time and StringBuilder.

But by using "+" you gain a factor of 6.

So now maybe you understand why premature optimization is useless :

- you have spent time making your code slower by trying to optimize it.

- you negleted to try the best performing solution anyway (using +), because of all the bashing of not using "+" for string concatenation and only considered 2 options with nearly the same performance. 

- you didn't use a benchmark or a profiler and then had no clue if your optimization what usefull at all.

Most of the time less than 10% of you code is responsible of more than 90% of the CPU cycle consumption.If you really need to optimize, concentrate of this code.

Make 90% of the code 2X faster may only give you a poor 5% increase, but making the 10% of the code that really count only 10% faster will give your better return (a gain of 9% in performance).

 

 

 

 

Jerry Gulla replied on Wed, 2011/01/19 - 9:21pm in response to: Nicolas Bousquet

This is absolutely the point. First off the JVM is *incredibly* efficient at object creation/cleanup (I've been in a few conference sessions with Brian Goetz where he has made this point) AND the JVM/Hotspot gets optimized for common usage patterns. Think "String 1" + "String 2" as pointed out above. Personally, I hate looking at code where there is a bunch of StringBuilder code that gets in the way of understanding what is going on, especially because many times it's not in something that was identified to have performance problems. We're still sticking to techniques that, while perhaps necessary when Java was first introduced, are in no way needed today.

Evan Summers replied on Thu, 2011/01/20 - 8:45am

whilst i use String.format(), i think MessageFormat with numbered varargs is better for internationalisation, eg. builder.appendf("{0} and {1}", hello, goodbye);

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.