|
Since |
Adriano Ferreira's Home Page |
|
Java Bit #1: Strings don't change |
Java strings are intransigent objects: they do not change no matter how much you ask them. By design, String objects are constant. When you apply operations to a String that produce other Strings, they are all different objects.
Java language specification calls for some optimization in compiling source to avoid useless construction of String objects that will have too short lives. For instance,
String sql = "SELECT " + f + " FROM " + t + " WHERE " + k + " = " + v;
is handled as the much more awkward piece of code that uses StringBuffer
instead of Strings.
StringBuffer sb = new StringBuffer();
sb.append("SELECT ").append(f).append(" FROM ").append(" WHERE ")
.append(k).append(" = ").append(v);
String sql = sb.toString();
This guarantees a much healthier performance than if 8 Strings were constructed only to be discarded. StringBuffers unlike Strings are buffers of chars that can be handled at will, appended, cut, scrambled.
Larger pieces of codes may however give such a hard time to Java compiler that there is not much option than produce Strings just for fun during processing.
Let's take a look at a very simple example: given an array of Strings, the task in hand is to write a method that produces a join of the array members with a delimiter in between. A naive approach would be to write a code such as this:
static String join0( String[] array, String delim ) {
String j = "";
for ( int i=0; i<array.length; i++ ) {
if (i!=0) j += delim;
j += array[i];
}
return j;
}
If the Java compiler do not infer that the construction of
array.length*2-2 Strings are just to return an
only String at the end of the method, this can be really bad.
That's the very reason why such a code is not considered good practice.
A more rational solution may look like this
static String join( String[] array, String delim ) {
StringBuffer sb = join(array, delim, new StringBuffer());
return sb.toString();
}
static StringBuffer join( String[] array, String delim, StringBuffer sb ) {
for ( int i=0; i<array.length; i++ ) {
if (i!=0) sb.append(delim);
sb.append(array[i]);
}
return sb;
}
with the additional advantage that the auxiliary method can be
used as it is to construct even longer character sequences before
a Java String is constructed.
For instance, imagine we are constructing a SELECT statement from given lists of select fields, tables to be joined (SQL 89), and parts of a WHERE clause:
StringBuffer sb = new StringBuffer();
sb.append("SELECT ");
sb.join(fields, ", ", sb);
sb.append("FROM ");
sb.join(tables, ", ", sb);
sb.append("WHERE ");
sb.join(filters, "AND ", sb);
String sql = sb.toString();