Mark Derricutt's Disturbing Thoughts

My Top Tags

                                       

My Jaiku

Chrome Division – Doomsday Rider

Thursday, 21 August 2008 5:43 A GMT+12

1 Session per VM: Another Scaling Alternative

Wednesday, 20 August 2008 9:47 P GMT+12

The BGGA myth - Functional Java | Google Groups

Wednesday, 20 August 2008 7:33 A GMT+12

Spock's Beard – On A Perfect Day (live)

Wednesday, 20 August 2008 5:57 A GMT+12

Ola Bini: JtestR 0.3.1 Released

Tuesday, 19 August 2008 10:00 P GMT+12

Enslaved – Violet Dawning

Tuesday, 19 August 2008 6:14 A GMT+12

Distributed Messaging with Jetlang and Terracotta

Monday, 18 August 2008 10:17 P GMT+12

The Music of 2008 - week 33

Monday, 18 August 2008 8:57 A GMT+12

There Can Be Only One

Monday, 18 August 2008 8:10 A GMT+12

Search Box

 

Writing Your Last For-Loop - Beautiful Code

posted Saturday, 20 October 2007
Writing Your Last For-Loop - Beautiful Code:
I was sitting with some friends the other week and a question came up. Someone asked, ???so, when did you last write a for-loop????

I remember reading this post the other day nodding to myself in agreement about how using closures and Java's new enhanced for loop makes the code a lot cleaner and easier to write, and then this evening a hit a bug in the TestNG IDEA plugin triggered by using this exact "improvement".

The problem with these closure/iterator-less loops in that when you use them you loose state information about the progress of your loop. The problem I hit with the plugin was with adding customer listeners to a test profile - the command line to launch TestNG was being generated as such:

-listener com.theoryinpractise.testng.requirements.RequirementsTransformer;

The problem? The trailing ; delimiter. When TestNG splits the list of listeners two items are returned: "com.theoryinpractise.testng.requirements.RequirementsTransformer" and "" of which the empty string triggers a ClassNotFoundException (one could argue this is a bug in three places - the split returning an empty string, TestNG not even attempting to load a class named "", and me for appending too many delimiters - we're all at fault imho).

But I digress, the code which triggered the problem:

if (data.TEST_LISTENERS != null && !data.TEST_LISTENERS.isEmpty()) {
  StringBuilder sb = new StringBuilder();
  for (String listenerClassName : data.TEST_LISTENERS) {
    if (listenerClassName != null && !"".equals(listenerClassName)) {
      sb.append(listenerClassName).append(";");
    }
  }
  javaParameters.getProgramParametersList().add(
    TestNGCommandLineArgs.LISTENER_COMMAND_OPT, sb.toString());
}

The code is simply appending ";" after the listener class name on each loop without considering we don't want a trailing separate.

The new enhanced for construct in Java 5 hides from the code any means to ask "is there anything to do after this iteration?" or even "what iteration am I on?". Common solutions to this would be to add code to trim the trailing ";", or increment a count variable on each iteration, all ways to reclaim some lost functionality.

So it seems there's still life in the old for loop yet.

Technorati Tags: , ,

tags:      

links: digg this    del.icio.us    technorati    reddit




1. Stig Brautaset left...
Monday, 22 October 2007 11:58 am :: http://brautaset.org

It's better practice, IMO, to just create an array of strings when going through the loop. (I.e. just do "sb.append(listenerClassName);", without appending the ";".) Then you simply join all the strings up with your chosen delimiter. Less chance of bugs that way, expressly _because_ you don't have to treat the first and last items differently. (It all boils down to lisp in the end.) Something like the following Perl pseudo-code:

my @nonempty = grep { $_ } $data->TEST_LISTENERS;

my $params = join(";", @nonempty);

(I've made the assumption here that Java has a way of joining a list/array of strings using a delimiter in a fashion similar to the above.)


2. OlliPlough left...
Monday, 22 October 2007 7:19 pm

I use iterators in that case. It's easy just to ask iterator.hasNext() and only add the ";" if true.


3. KiLVaiDeN left...
Tuesday, 23 October 2007 1:53 am

Well, the "enhanced" for loop for iterating through Collections is not supposed to be a replacement for the already existing loop, it's just an add-on.

As you say, sometimes you need a counter, sometimes you need to explicitely make the exit based on a condition, etc... It all depends on what you need :) So the classic for is definitly not going to the void yet ;)

On my own, when I need to create a delimited list of Strings without the last delimiter included, i've created a join method, which takes a String array and a delimiter, I just append them to a StringBuilder, and then in the end, I remove the length of the delimiter. Fast enough for my needs ( don't know if you can make any faster ).

Cheers K


4. Jeremy Weiskotten left...
Tuesday, 23 October 2007 3:43 am :: http://jeronrails.blogspot.com

The Ruby-ish way of doing this is:

delimited_string = TEST_LISTENERS.reject(&:blank?).join(';')

Closures allow you to approach iteration-type problems differently.