Dougnukem Dougnukem - 24 days ago 8
Java Question

Best Loop Idiom for special casing the last element

I run into this case a lot of times when doing simple text processing and print statements where I am looping over a collection and I want to special case the last element (for example every normal element will be comma separated except for the last case).

Is there some best practice idiom or elegant form that doesn't require duplicating code or shoving in an if, else in the loop.

For example I have a list of strings that I want to print in a comma separated list. (the do while solution already assumes the list has 2 or more elements otherwise it'd be just as bad as the more correct for loop with conditional).

e.g. List = ("dog", "cat", "bat")

I want to print "[dog, cat, bat]"

I present 2 methods the


  1. For loop with conditional

    public static String forLoopConditional(String[] items) {


    String itemOutput = "[";


    for (int i = 0; i < items.length; i++) {
    // Check if we're not at the last element
    if (i < (items.length - 1)) {
    itemOutput += items[i] + ", ";
    } else {
    // last element
    itemOutput += items[i];
    }
    }
    itemOutput += "]";


    return itemOutput;
    }

  2. do while loop priming the loop

    public static String doWhileLoopPrime(String[] items) {
    String itemOutput = "[";
    int i = 0;


    itemOutput += items[i++];
    if (i < (items.length)) {
    do {
    itemOutput += ", " + items[i++];
    } while (i < items.length);
    }
    itemOutput += "]";


    return itemOutput;
    }


    Tester class:

    public static void main(String[] args) {
    String[] items = { "dog", "cat", "bat" };


    System.out.println(forLoopConditional(items));
    System.out.println(doWhileLoopPrime(items));


    }



In the Java AbstractCollection class it has the following implementation (a little verbose because it contains all edge case error checking, but not bad).

public String toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";

StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}

Answer

There are a lot of for loops in these answers, but I find that an Iterator and while loop reads much more easily. E.g.:

Iterator<String> itemIterator = Arrays.asList(items).iterator();
if (itemIterator.hasNext()) {
  // special-case first item.  in this case, no comma
  while (itemIterator.hasNext()) {
    // process the rest
  }
}

This is the approach taken by Joiner in Google collections and I find it very readable.

Comments