Should try...catch go inside or outside a loop?

Solution 1:

PERFORMANCE:

There is absolutely no performance difference in where the try/catch structures are placed. Internally, they are implemented as a code-range table in a structure that is created when the method is called. While the method is executing, the try/catch structures are completely out of the picture unless a throw occurs, then the location of the error is compared against the table.

Here's a reference: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

The table is described about half-way down.

Solution 2:

Performance: as Jeffrey said in his reply, in Java it doesn't make much difference.

Generally, for readability of the code, your choice of where to catch the exception depends upon whether you want the loop to keep processing or not.

In your example you returned upon catching an exception. In that case, I'd put the try/catch around the loop. If you simply want to catch a bad value but carry on processing, put it inside.

The third way: You could always write your own static ParseFloat method and have the exception handling dealt with in that method rather than your loop. Making the exception handling isolated to the loop itself!

class Parsing
{
    public static Float MyParseFloat(string inputValue)
    {
        try
        {
            return Float.parseFloat(inputValue);
        }
        catch ( NumberFormatException e )
        {
            return null;
        }
    }

    // ....  your code
    for(int i = 0; i < max; i++) 
    {
        String myString = ...;
        Float myNum = Parsing.MyParseFloat(myString);
        if ( myNum == null ) return;
        myFloats[i] = (float) myNum;
    }
}

Solution 3:

All right, after Jeffrey L Whitledge said that there was no performance difference (as of 1997), I went and tested it. I ran this small benchmark:

public class Main {

    private static final int NUM_TESTS = 100;
    private static int ITERATIONS = 1000000;
    // time counters
    private static long inTime = 0L;
    private static long aroundTime = 0L;

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TESTS; i++) {
            test();
            ITERATIONS += 1; // so the tests don't always return the same number
        }
        System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
        System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
    }
    public static void test() {
        aroundTime += testAround();
        inTime += testIn();
    }
    public static long testIn() {
        long start = System.nanoTime();
        Integer i = tryInLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static long testAround() {
        long start = System.nanoTime();
        Integer i = tryAroundLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
    public static Integer tryAroundLoop() {
        int count = 0;
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            }
            return count;
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}

I checked the resulting bytecode using javap to make sure that nothing got inlined.

The results showed that, assuming insignificant JIT optimizations, Jeffrey is correct; there is absolutely no performance difference on Java 6, Sun client VM (I did not have access to other versions). The total time difference is on the order of a few milliseconds over the entire test.

Therefore, the only consideration is what looks cleanest. I find that the second way is ugly, so I will stick to either the first way or Ray Hayes's way.

Solution 4:

While performance might be the same and what "looks" better is very subjective, there is still a pretty big difference in functionality. Take the following example:

Integer j = 0;
    try {
        while (true) {
            ++j;

            if (j == 20) { throw new Exception(); }
            if (j%4 == 0) { System.out.println(j); }
            if (j == 40) { break; }
        }
    } catch (Exception e) {
        System.out.println("in catch block");
    }

The while loop is inside the try catch block, the variable 'j' is incremented until it hits 40, printed out when j mod 4 is zero and an exception is thrown when j hits 20.

Before any details, here the other example:

Integer i = 0;
    while (true) {
        try {
            ++i;

            if (i == 20) { throw new Exception(); }
            if (i%4 == 0) { System.out.println(i); }
            if (i == 40) { break; }

        } catch (Exception e) { System.out.println("in catch block"); }
    }

Same logic as above, only difference is that the try/catch block is now inside the while loop.

Here comes the output (while in try/catch):

4
8
12 
16
in catch block

And the other output (try/catch in while):

4
8
12
16
in catch block
24
28
32
36
40

There you have quite a significant difference:

while in try/catch breaks out of the loop

try/catch in while keeps the loop active

Solution 5:

I agree with all the performance and readability posts. However, there are cases where it really does matter. A couple other people mentioned this, but it might be easier to see with examples.

Consider this slightly modified example:

public static void main(String[] args) {
    String[] myNumberStrings = new String[] {"1.2345", "asdf", "2.3456"};
    ArrayList asNumbers = parseAll(myNumberStrings);
}

public static ArrayList parseAll(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        myFloats.add(new Float(numberStrings[i]));
    }
    return myFloats;
}

If you want the parseAll() method to return null if there are any errors (like the original example), you'd put the try/catch on the outside like this:

public static ArrayList parseAll1(String[] numberStrings){
    ArrayList myFloats = new ArrayList();
    try{
        for(int i = 0; i < numberStrings.length; i++){
            myFloats.add(new Float(numberStrings[i]));
        }
    } catch (NumberFormatException nfe){
        //fail on any error
        return null;
    }
    return myFloats;
}

In reality, you should probably return an error here instead of null, and generally I don't like having multiple returns, but you get the idea.

On the other hand, if you want it to just ignore the problems, and parse whatever Strings it can, you'd put the try/catch on the inside of the loop like this:

public static ArrayList parseAll2(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        try{
            myFloats.add(new Float(numberStrings[i]));
        } catch (NumberFormatException nfe){
            //don't add just this one
        }
    }

    return myFloats;
}