Java: Unstrung
So Strings are built for optimisation, but are you using it in an optimised way? If you understand interning and immutability, you can start looking at ways to use those features to your advantage.
How to Waste Memory Without Really Trying
Thinking about a String’s immutability, let’s look at String concatenation:
String name = getName(); //Returns "Joe"
String a = "Welcome ";
a += name;
a += ", Good to See You.";
Okay, how many Objects did we create?
Seven. One for name
, one for "Welcome "
when first assigned to a
, one for "Welcome Joe"
on the first concatenation, one for ", Good to See You."
, and one for the final String assigned to a, "Welcome Joe, Good to See You."
. Not only does it create all those Strings, it also creates two StringBuffer Objects. Since you can’t change a String, the JVM needs to create an object that allows you to concatenate Strings. So for:
a += name;
the JVM creates a StringBuffer Object that equals a
, appends name
and then converts back to a String and assigns it to a
.
In fact, for every += you see, there is a StringBuffer Object (a rather expensive object to create) that is being created, used for one operation, and then being thrown away. And that happens every time this code it run. That adds up quick on a busy program and will make the Garbage Collector run much more often than it should.
A better way to do this would be:
StringBuffer buffer = new StringBuffer();
buffer.append("Welcome ");
buffer.append(getName());
buffer.append(", Good to See You.");
String a = buffer.toString();
Not only does this create less Objects, is also calls less functions during the process. This is important enough that the compiler tries to do some of this for you. The below example is equal to the StringBuffer example:
String a = "Welcome " + getName() + ", Good to See You.";
And when I say equal, I mean exactly equal. The resulting bytecode is exactly the same. This is because we’re using +, which the compiler converts to a StringBuffer first.
So the key is, Never use += with Strings. Use a StringBuffer instead.
Again, Again
One more point that has to do with interning. Interning only works with String literals like:
String a = "Welcome ";
If you create a String like this is won’t be interned:
String a = new String("Welcome ");
This actually creates two Strings; one that’s interned and one that’s not. Do you need two Strings equal to "Welcome "
? No, you don’t, so Never create a String with a constructor (new String()).
For more info, see http://www.javaworld.com/javaworld/jw-03-2000/jw-0324-javaperf.html