String Concatenation Options

There's a new inspection in IDEA 8 (might just be in the EAP at this point) that will convert string concatentation to a variety of different approaches.  One of these options is to use String.format().  I started applying this option to some code I'm working because it's certainly more readable than some of the concatenation stuff I'd been doing.  But I started thinking that I should probably profile this before I get too crazy with it just to make sure I'm not hamstringing myself with this.  So I wrote a simple test to see what the fastest option was and I was a little surprised by the results. First, let's see the code.

import java.text.*;

public class test {
	private static long concat(int count) {
		long start = System.currentTimeMillis();
		for(int x = 0; x < count; x++) {
			String s = "Loop " + x + " of " + count + " iterations.";
		}
		return System.currentTimeMillis() - start;
	}

	private static long format(int count) {
		long start = System.currentTimeMillis();
		for(int x = 0; x < count; x++) {
			String s = String.format("Loop %s of %s iterations.", x, count);
		}
		return System.currentTimeMillis() - start;
	}

	private static long format2(int count) {
		long start = System.currentTimeMillis();
		for(int x = 0; x < count; x++) {
			String s = MessageFormat.format("Loop {0} of {1} iterations." , x, count);
		}
		return System.currentTimeMillis() - start;
	}

	private static long append(int count) {
		long start = System.currentTimeMillis();
		for(int x = 0; x < count; x++) {
			String s = new StringBuilder("Loop ")
				.append(x)
				.append(" of ")
				.append(count)
				.append("iterations.")
				.toString();
		}
		return System.currentTimeMillis() - start;
	}

	public static void main(String[] args) {
		int count = 1000000;

		for(int x = 0; x < 10; x++) {
			System.out.println("concat = " + concat(count));
			System.out.println("String.format = " + format(count));
			System.out.println("MessageFormat.format = " + format2(count));
			System.out.println("append = " + append(count));
			System.out.println();
		}
	}
}

This admittedly naive "benchmark" runs through four options and prints out the basic timing results.  I've compiled the results below in a table:

concat String.format MessageFormat.format append
408 3164 9099 376
338 2876 8559 340
300 3013 8655 398
342 2938 8511 311
308 2911 8570 310
306 2924 8726 320
316 3019 9006 414
306 2994 8673 331
346 3022 9588 311
312 2988 8590 313

As you can see both format methods take considerably more time than the other two.  I was a little surprised to see this though the magnitude of the difference was more surprising than than the difference itself.  So neither of those are options you'll want to consider in areas that get called often.  The one that was really suprising for me was the relative similarity between concatenating strings and appending using StringBuilder.  While StringBuilder was generally faster than string concats, the difference was rather minimal and in some runs actually slower.  What this says to me is that the generally accepted "wisdom" that String concatenation is slower than using StringBuilder is clearly wrong.

On the other, I'm not really a performance expert so I might be doing something stupid here or missing something fundamental.  The test seems rather straightforward, though.  What do you think?

Dealing with NullPointerExceptions

One of the most common problems that begginers run into (and some 'experts' though they can handle it i should hope) is the dreaded NullPointerException.  One of the most frustrating things about the NPE is that it doesn't say what was null.  All you get is a line number in the stack trace so if you're doing a lot on that line, it's not always clear.  There are various ideas about how to clean that up in java 7.  There are other solutions here and there as well so we'll see if any of that makes it into Java 7 or not.  But we don't have Java 7 yet so none of that really helps.  So here's my methodology/suggestions for dealing with them now. First, let's list why NPEs happen.  That will help you find many NPEs just by looking at the code.  The most common (and obvious to the seasoned developer) is that you didn't initialize a variable.  Now, local variables must be initialized so the compiler helps you out a little there.  However, instance and class fields do not so you'll need to be careful there.  Also, you can silence the compiler complaints about uninitialized local variables by setting them to null.  Obviously, if you don't reassign these references before trying to use them, you'll get an NPE.

Another source is method return values.  If you can't see the code being called, there's no real guarantee that you'll get a non-null reference back out of it.  To be safe, all these values should be check for nulls before using them.  Of course, your own methods might have bugs in them such that an null return might not be obvious.  Or it might be intentional.  In any case, you need to be mindful of the risks of using return values.

Dealing with them is simple enough though apparently not that obvious to a beginner.  As I mentiond earlier, the exact line is mentioned in the stack trace in the error logs or on the console.  Given the discussion above, you can often spot the offending reference just by looking at that line of code.  But if you do a lot of things one a line of code like I usually do, it can sometimes be less than obvious.  The solution is simple enough here, too:  break the line down into simpler bits.  If you have chained method calls, for example, create local variables for each step and print out the results:

foo.bar().bob(getDoug()).dude(getCar());

This becomes:

System.out.println("foo = " + foo);
Bar bar = foo.bar();
System.out.println("bar = " + bar);
Doug doug = getDoug();
System.out.println("doug = " + doug);
Bob bob = bar.bob(doug);
System.out.println("bob = " + bob);

We can essentially rule out the results of getCar() as that would result in an NPE on another line if that null gets passed into dude().  But this should be enough to highlight the exact value that is null.  Once you correct that, you can recombine all that back into online if you like.  There you go.  NPE found and fixed.

It's nothing fancy or complex.  Just a little legwork to get you over the hump.  This sort of thing becomes second nature to seasoned programmers but isn't always the most obvious to beginners.  I hope it helps some of you on your way.

Basic Thread Advice

I'm not a concurrency expert and you won't find anything terribly profound if you consider yourself an experienced multithreaded developer.  But I did want to share a simple, basic tip for beginners.  Often when a beginner decides that threads are the solution to whatever problem he's facing, he extends Thread.  When told that extending Thread is a bad idea and that he should implement Runnable instead, it often falls on deaf ears.  Most of the reasoning given is that there's no point in extending Thread and that you use up your one extension for no gain.  This is all true but not entirely compelling.  This morning I ran across a much more compelling argument:  restarting a thread. Threads, once stopped, can not be restarted so you have to create a new Thread and start that one.  Normally even this may not be that big of a deal.  But in this particular case this morning, the developer had state he wanted to preserve.  Since his Thread could not be restarted, he'd have to create a new Thread, copy over that state, then start the new one.  If he'd just used a Runnable, he could have simply started a new Thread with that Runnable and be done.  Faced with that realization, he changed his code to use Runnables instead and is now a happy camper.

So, again, don't extend Thread.  You gain nothing from it and tie your hands in more than one way.