我有一个这样的循环:

for (int i = 0; i < max; i++) {
    String myString = ...;
    float myNum = Float.parseFloat(myString);
    myFloats[i] = myNum;
}

这是一个方法的主要内容,该方法的唯一目的是返回浮点数数组。我想让这个方法在出现错误时返回null,所以我把循环放在try…Catch block,像这样:

try {
    for (int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    }
} catch (NumberFormatException ex) {
    return null;
}

但后来我也想到试一试……Catch块在循环中,像这样:

for (int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
    } catch (NumberFormatException ex) {
        return null;
    }
    myFloats[i] = myNum;
}

是否有任何理由,性能或其他方面,更喜欢其中一个?


编辑:共识似乎是,将循环放在try/catch中更干净,可能是在它自己的方法中。然而,关于哪个速度更快仍存在争议。有人能测试一下并给出一个统一的答案吗?


当前回答

如果这是一个全有或全无的失败,那么第一种格式是有意义的。如果希望能够处理/返回所有未失败的元素,则需要使用第二种形式。这些就是我选择方法的基本标准。就我个人而言,如果是孤注一掷,我不会使用第二种形式。

其他回答

性能:就像Jeffrey在他的回复中所说的,在Java中这并没有太大的区别。

通常,为了代码的可读性,在哪里捕获异常的选择取决于您是否希望循环继续处理。

在您的示例中,您在捕获异常时返回。在这种情况下,我会在循环中放入try/catch。如果你只是想捕捉一个坏值,但继续处理,把它放在里面。

第三种方法:您总是可以编写自己的静态ParseFloat方法,并在该方法中而不是在循环中处理异常处理。使异常处理与循环本身隔离!

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;
    }
}

My perspective would be try/catch blocks are necessary to insure proper exception handling, but creating such blocks has performance implications. Since, Loops contain intensive repetitive computations, it is not recommended to put try/catch blocks inside loops. Additionally, it seems where this condition occurs, it is often "Exception" or "RuntimeException" which is caught. RuntimeException being caught in code should be avoided. Again, if if you work in a big company it's essential to log that exception properly, or stop runtime exception to happen. Whole point of this description is PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

好吧,在Jeffrey L Whitledge说没有性能差异之后(截至1997年),我去测试了一下。我运行了一个小的基准测试:

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;
        }
    }
}

我使用javap检查了结果字节码,以确保没有任何内容得到内联。

结果表明,假设JIT优化微不足道,Jeffrey是正确的;在Java 6、Sun客户端VM上绝对没有性能差异(我没有访问其他版本)。整个测试的总时间差在几毫秒的数量级上。

因此,唯一要考虑的是什么看起来最干净。我发现第二种方式很难看,所以我要么坚持第一种方式,要么坚持雷·海耶斯的方式。

虽然性能可能是相同的,“看起来”更好的是非常主观的,但在功能上仍然有相当大的差异。举个例子:

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");
    }

while循环位于try catch块内,变量'j'将递增到40,当j mod 4为零时输出,当j达到20时抛出异常。

在详细介绍之前,先来看另一个例子:

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"); }
    }

与上面的逻辑相同,唯一不同的是try/catch块现在在while循环中。

下面是输出(在try/catch中):

4
8
12 
16
in catch block

而另一个输出(try/catch in while):

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

这里有很大的不同:

While in try/catch跳出循环

Try /catch in while保持循环活动

在你的例子中没有功能上的区别。我觉得你的第一个例子可读性更强。