小编典典

应该尝试...catch 进入循环内部还是外部?

all

我有一个看起来像这样的循环:

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

这是唯一目的是返回浮点数组的方法的主要内容。null如果出现错误,我希望此方法返回,因此我将循环放在一个try...catch块中,如下所示:

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

但是后来我也想到了将try...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
中更干净,可能放在它自己的方法中。但是,仍然存在关于哪个更快的争论。有人可以对此进行测试并给出统一的答案吗?


阅读 64

收藏
2022-07-01

共1个答案

小编典典

好吧,在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 上
绝对没有性能差异(我无法访问其他版本)。在整个测试中,总时间差大约为几毫秒。

因此,唯一的考虑是看起来最干净的东西。我发现第二种方式很难看,所以我会坚持第一种方式或Ray Hayes的方式。

性能:在 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;
    }
}
2022-07-01