Sunday, November 15, 2015

There's a loop for each of us

This is one of the really useful additions to the Java language specifications.  It greatly simplifies many loop structures, eliminating a bunch of verbiage that we long thought was just part of the deal when writing code.

Let's meet what I hope will become a good friend of yours:  The enhanced for, also known as the 'for each' loop.

The for each loop structure is an enhancement that can be used to replace many of the standard for or while loops you would otherwise be writing.  It is extremely common to have to, for instance, go over the elements of an array or a collection, extract one element at a time, and perform operations on it.  Let's pretend that we have an ArrayList of Person objects, and we want to print a list of first and last names.

In the old days we might have written this (and if you don't understand why I use 'List' instead of 'ArrayList' see 'Good Practices: Coding to the Interface'):

public static void printPersonList1(List personList) {
    System.out.println("Using a 'while' loop the JDK 1.4 (and earlier) way:");
    Iterator iter = personList.iterator();
    while(iter.hasNext()) {
        Person p = (Person) iter.next();
        System.out.println(p.getFirstName() + " " + p.getLastName());
    }
}

Now, one improvement was the addition of 'Generics', which allowed us to simplify the code a bit by informing the compiler that our List and Iterator had to be of type Person:

public static void printPersonList2(List personList) {
    System.out.println("Using a 'while' loop with generics:");
    Iterator <Person> iter = personList.iterator();
    while(iter.hasNext()) {
        Person p = iter.next();
        System.out.println(p.getFirstName() + " " + p.getLastName());
    }
}

Now, you might be wondering why I did my demonstrations of old style code with while loops to demonstrate a better for loop.  This is mostly because the while loop syntax is better suited to running over an Iterator than an old style for loop was.  It could be done, but frankly it's not as clean.  You'll note I had to leave out the third term of the for loop because it only gets executed after the body of the loop is complete and I had to pre-seed with the first value:

public static void printPersonList3(List<Person> personList) {
    System.out.println("Using a 'for' loop with generics:");
    Person p = null;
    for(Iterator <Person> iter = personList.iterator(); iter.hasNext(); ) {
        p = iter.next();
        System.out.println(p.getFirstName() + " " + p.getLastName());
    }
}


Alternately, we could have skipped the Iterator altogether, which is really more in keeping with the natural usage of the old style for loop:

public static void printPersonList4(List<Person> personList) {
    System.out.println("Using a 'for' loop with no iterator:");
    Person p = null;
    for(int i = 0; i < personList.size(); i++) {
        p = personList.get(i);
        System.out.println(p.getFirstName() + " " + p.getLastName());
    }
}


OK, here's the actual example I'm trying to get across to you.  This is the 'enhanced for' loop, also knows as the 'for each':

But the for each loop construct went a lot further and made things much better:

public static void printPersonList5(List<Person> personList) {
    System.out.println("Using a 'for each' loop:");
    for(Person p:personList) {
        System.out.println(p.getFirstName() + " " + p.getLastName());
    }
}

Note how concise it is, with no extra stuff.  Get Person objects from this collection and run with it.  Using the enhanced for loop simplifies our code, letting us get on with the conceptual task at hand and saving our fingers from some typing.  It's extremely common to go over a collection of data and perform some task on every element, so why should we have to write it all out explicitly every time?  You can use for each loops on classes based on Collection and on arrays equally well, which means we're more likely to use it and get the code right the first time.

private static void printStringArray(String[] aList) {
    System.out.println("The enhanced for can also work on array types:");
    for(String s: aList) {
        System.out.println(s);
    }
    System.out.println();
}

It actually took me a while to get used to using this loop structure, because I spent so much time supporting code that had to run in 1.4 level JREs.  In order to ensure the best compatibility I was also compiling with a 1.4 JDK so I didn't have access to this feature.  I'll tell you what, I was really missing out.  My fingers are probably a couple of millimeters shorter than they should be because of all the wear and tear from the extra typing I did.  I for one am glad we have this loop available, and you probably should be, too.

View Code