1. ホーム
  2. java

[解決済み】try...catchはループの内側と外側のどちらで行うべきですか?

2022-04-19 15:43:44

質問

次のようなループがあります。

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

これは、floatの配列を返すことだけを目的としたメソッドのメインコンテンツです。このメソッドで返したいのは 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の中、場合によっては独自のメソッドの中に入れた方がすっきりするというのがコンセンサスになっているようです。しかし、どちらがより速いかについてはまだ議論があります。誰かこれをテストして、統一された答えを持って来ることができますか?

解決方法は?

さて、この後 ジェフリー・L・ウィットレッジのコメント 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最適化を前提とした場合、以下のようになりました。 ジェフリーは正しい は絶対にあります。 Java 6、SunクライアントVMで性能差なし (他のバージョンにはアクセスできませんでした)。テスト全体の時間差は、数ミリ秒のオーダーです。

ですから、一番きれいに見えるのはどれか、ということだけを考えればいいのです。2番目の方法は醜いので、1番目の方法か、あるいは レイ・ヘイズの方法 .