I finally broke down and bought a Kindle. I’ve been eyeballing them since the first one came out and have been daydreaming about them since the Kindle 2 pics first leaked. After reading countless previews and reviews and raves and rants, I decided it was time. Sure, it’s expensive. Yeah, it’s “only a single function device.” blahblahblah. The fact is I love to read, I live in a NYC apartment, and I already have an entire wall devoted to bookshelves crammed full of my books. There’s just not that much space here to keep buying more and more books. Any my library hardly ever has what I want on the shelf. When they have it at all, there’s a waiting list. So this makes a lot of sense for me in a number of ways. I’ve had it for about an hour now so I don’t have any deep dive experience with it as such, but as far as first impressions go, it’s a big win. And the first thing I did after browsing through the user’s guide? I bought Brandon Sanderson‘s latest book “The Hero of the Ages.” Now if only I didn’t have to work.
04
2009
23
2009
String Concatenation Revisited
I had intended to do some follow up numbers to my previous post but I got a bit sidetracked by work and the like. My simple tests all work with one String that’s created then thrown away. This test helped me resolve the question I had when I started down that road but stops short of a more general answer. Then I saw this pingback which led me here. There’s some nice analysis and insights to consider. So given the shortcomings of my little benchmark and the comments there, I wanted to expand my test a bit and see what things look like when the loop doesn’t throw away the data. The test is simple enough again:
import java.util.*; import java.text.*; public class ConcatenationTest { 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 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; } private static long concatAcrossLoops(int count) { long start = System.currentTimeMillis(); String s = ""; for(int x = 0; x < count; x++) { s += "Loop " + x + " of " + count + " iterations."; } long time = System.currentTimeMillis() - start; System.out.println("concatAcrossLoops time = " + time); return time; } private static long appendAcrossLoops(int count) { long start = System.currentTimeMillis(); StringBuilder s = new StringBuilder(); for(int x = 0; x < count; x++) { s.append("\nLoop ") .append(x) .append(" of ") .append(count) .append("iterations."); } long time = System.currentTimeMillis() - start; System.out.println("appendAcrossLoops time = " + time); return time; } public static void main(String[] args) { int count = 10000; List concats = new ArrayList(); List appends = new ArrayList(); List concatsAcross = new ArrayList(); List appendsAcross = new ArrayList(); for(int x = 0; x < 10; x++) { concats.add(concat(count)); appends.add(append(count)); concatsAcross.add(concatAcrossLoops(count)); appendsAcross.add(appendAcrossLoops(count)); } String header = "concats appends concats across loops appends across loops"; String format = "%7d %9d %22d %22d\n"; System.out.println(header); for(int x = 0; x < 10; x++) { System.out.printf(format, concats.get(x), appends.get(x), concatsAcross.get(x), appendsAcross.get(x)); } } } |
And then the results:
| concats | appends | concats across loops | appends across loops |
|---|---|---|---|
| 48 | 14 | 18990 | 1276 |
| 27 | 11 | 14581 | 1442 |
| 4 | 4 | 13206 | 1253 |
| 3 | 3 | 13478 | 1438 |
| 4 | 4 | 12651 | 1444 |
| 4 | 3 | 12485 | 1403 |
| 4 | 3 | 12608 | 1318 |
| 4 | 3 | 13152 | 1312 |
| 3 | 4 | 12535 | 1390 |
| 4 | 3 | 12444 | 1329 |
Notice after the first two loops the numbers for all runs drops. As the JIT compiler kicks in, we get some optimization but as you can see concatenation across loop iterations is incredibly much more expensive. In this case, StringBuilder is still the clear winner.
update
There was a typo in the original test. I was calling toString() in the appendsAcrossLoop test which was entirely unnecessary. (I forgot to remove that call when adapting from the earlier iteration.) The new results are below. I included them here rather than just replacing the table above as it shows just how expensive that toString() is.
| concats | appends | concats across loops | appends across loops |
|---|---|---|---|
| 42 | 15 | 16562 | 4 |
| 5 | 8 | 12564 | 5 |
| 4 | 3 | 11601 | 2 |
| 4 | 2 | 11141 | 2 |
| 4 | 3 | 11025 | 3 |
| 3 | 3 | 11260 | 3 |
| 3 | 3 | 11062 | 3 |
| 3 | 3 | 11738 | 2 |
| 4 | 2 | 11078 | 2 |
| 4 | 2 | 11130 | 3 |
09
2009
Isn't it time for GCJ to die?
What’s the motivation for gcj these days? Originally, everyone wanted a GPLd JVM so gcj kinda made sense. At least in spirit. It’s never been a functional equivalent for an actual JVM, though. I’ve seen nothing but problems with it for years in IRC channels. It’s partial implementation of the spec has led to endless confusion for uncounted newbies coming to the java channel for help. It doesn’t help that the ideologues at Debian, et. al, continue to package gcj as if it were java. Well, we have a GPLd JVM now. Everything about it is open source (or just about done…).
GCJ, as I see it, serves no more useful purpose than allowing those in charge of it to hold on to some ideal (or maybe pride). I know this is inflammatory for a good number of people, but why persist? Is it the native compilation you like? The slow, misbegotten catastrophe that it is? It’s slower than running java bytecode and seems to eliminate several key features of Java (like dynamic classloading). Even before Sun GPLd their (our? I’m a Sun guy after all…) JVM, gcj adoption was miniscule at best. So, what’s the point? Can’t we move on from gcj? Or at a minimum, stop packaging it as the default JVM when it’s not actually a java implementation? That’d work for me. I’m just tired of seeing newbies getting tripped up by some distro’s ideological navel gazing.
05
2009
Changing the Current Directory
One of the more common requests I see online from beginners (and from a not-so-beginner just now) is how to change the current directory. This one is really simple, so here’s a quick snippet and the output.
System.out.println(new File(“.”).getAbsolutePath());
System.setProperty(“user.dir”, System.getProperty(“java.io.tmpdir”));
System.out.println(new File(“.”).getAbsolutePath());
And the output:
/Users/jlee/.
/tmp/.
See? Simple.
29
2009
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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?
23
2009
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.
21
2009
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.
20
2009
How Best to Prepare and Serve Crow
I’ve been very vocal about my distaste for maven in the past. Most of my preference for other tools is the large, spiky, opaque interface that maven offers. Figuring out how to customize a maven build is an exercise in frustration at best. I was talking to a maven dev (no clue how “senior” the guy was) and he couldn’t answer a question I had (I think it was about finding out what plugins/targets a pom offered). He fumbled about looking for an answer. And that just typifies why I dislike maven so much.
On the other hand, there are many things to like as well. IDE integration has finally reached a usable point for me. IDEA has top notch support for basing a project definition off of it, finally, which is a big win if your project has dependency changes frequently. It also makes hetergeneous development environments much more portable among many other advantages. Maven still has its Golden Path on which life is all rainbows and butterflies and deviating from that path is starts to look like a descent into Emyn Muil. But still, the world is slowly moving that way in large part because the ant guys seem to have stopped any sort of evolution of their own offering.
So it’s my intention to move all my stuff to maven now. We use it at work so I’m already using it most of the time anyway. Many of my complaints are born of frustration/ignorance of the details of the tool and there’s only way to cure that. I knew that despite all my kvetching that this day would come. And here it is.
So I have one question for the tube generation: what’s the best way to cook crow?
18
2009
New old article available
I’m in the middle writing of another article and remembered I had an older one that got dropped in a site redesign. People have been asking for it but it always seems to drop off my radar. Well, I finally put it back online. You can also find it in the page header later if you’d like. It’s a bit dated but most of it still applies. Hopefully it’ll still be helpful to others. I should probably devote some “ink” to testng and maven since that is it own special little hell but that’s for another time.
17
2008
Being a good citizen?
The choice of build tools is surprisingly contentious. I know I, personally, am not a huge maven fan. I’ve been given to explosive, vitriolic rants against it, actually. But now I’m forced to use it at work so I’m learning it more than I’d hoped to ever have to.
But whatever your choice of build tool is, there’s one thing I think should happen that would help out so many people. If you write some form of library to be reused by the world at large, it should get published in the maven repos. Or maybe an ivy repo(s) if there are such centralized/public beasts. Making it easy to find these artifacts will only help to drive adoption of a project.
Whether you chose maven or ant at that point (or something else…), it becomes quite simple to find and fetch dependencies. I use the ant tasks from the maven project to pull down my deps and then ant to build. It works well enough for me until I run into a library that hasn’t been published to a repository somewhere. Some projects are really good about publishing their artifacts. Other projects’ devs respond with simple “not my problem” responses. If I can’t find a dependency in maven, I’m much more likely to try to find another library to use. So uploading an artifact may not be your problem, but it’s certainly not mine. It’s a good way to drive people like me to other projects, though. But maybe you don’t care about that, either.