Стоит ли пытаться ... поймать иди внутри или снаружи петли? - PullRequest
171 голосов
/ 26 сентября 2008

У меня есть цикл, который выглядит примерно так:

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, возможно, внутри собственного метода. Тем не менее, есть еще споры о том, что быстрее. Может кто-нибудь проверить это и вернуться с единым ответом?

Ответы [ 21 ]

2 голосов
/ 14 февраля 2019

С моей точки зрения, блоки try / catch необходимы для обеспечения надлежащей обработки исключений, но создание таких блоков влияет на производительность. Поскольку циклы содержат интенсивные повторяющиеся вычисления, не рекомендуется помещать блоки try / catch внутри циклов. Кроме того, кажется, что, где возникает это условие, часто это «Исключение» или «RuntimeException», который перехватывается. Следует избегать попадания RuntimeException в код. Опять же, если вы работаете в большой компании, важно правильно зарегистрировать это исключение или прекратить возникновение исключения во время выполнения. Весь смысл этого описания PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS

2 голосов
/ 26 сентября 2008

В ваших примерах нет функциональной разницы. Я считаю ваш первый пример более читабельным.

1 голос
/ 26 сентября 2008

Если он внутри, вы получите накладные расходы структуры try / catch N раз, а не один раз снаружи.


Каждый раз, когда вызывается структура Try / Catch, это увеличивает накладные расходы на выполнение метода. Просто немного памяти и тиков процессора, необходимых для работы со структурой. Если вы выполняете цикл 100 раз и, предположительно, скажем, стоимость составляет 1 тик за вызов try / catch, то наличие Try / Catch внутри цикла обойдется вам в 100 тиков, а не 1 тик, если вне петли.

1 голос
/ 11 марта 2010

Другим аспектом, не упомянутым выше, является тот факт, что каждый try-catch оказывает некоторое влияние на стек, что может иметь значение для рекурсивных методов.

Если метод "external ()" вызывает метод "inner ()" (который может вызывать себя рекурсивно), попробуйте найти try-catch в методе "outer ()", если это возможно. Простой пример «сбоя стека», который мы используем в классе производительности, завершается с ошибкой около 6400 кадров, когда try-catch находится во внутреннем методе, и около 11 600, когда он находится во внешнем методе.

В реальном мире это может быть проблемой, если вы используете шаблон Composite и имеете большие сложные вложенные структуры.

1 голос
/ 27 сентября 2008

установка специального фрейма стека для try / catch добавляет дополнительные издержки, но JVM может обнаружить факт возврата и оптимизировать его.

В зависимости от количества итераций разница в производительности, вероятно, будет незначительной.

Тем не менее, я согласен с другими, что наличие его вне петли делает тело петли более чистым.

Если есть вероятность, что вы когда-нибудь захотите продолжить обработку, а не завершить работу, если найдется недопустимый номер, тогда вы бы хотели, чтобы код был внутри цикла.

1 голос
/ 27 сентября 2008

Весь смысл исключений состоит в том, чтобы поощрять первый стиль: позволить обработке ошибок консолидироваться и обрабатываться один раз, а не сразу на каждом возможном месте ошибки.

1 голос
/ 09 октября 2008

Я положу свои 0,02 доллара. Иногда вам приходится добавлять «наконец» позже в ваш код (потому что, кто когда-либо отлично пишет свой код в первый раз?). В этих случаях внезапно становится более целесообразным использовать try / catch вне цикла. Например:

try {
    for(int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        dbConnection.update("MY_FLOATS","INDEX",i,"VALUE",myNum);
    }
} catch (NumberFormatException ex) {
    return null;
} finally {
    dbConnection.release();  // Always release DB connection, even if transaction fails.
}

Потому что, если вы получили ошибку или нет, вы хотите разорвать соединение с базой данных (или выбрать свой любимый тип другого ресурса ...) один раз.

1 голос
/ 07 октября 2008

Это зависит от обработки ошибок. Если вы просто хотите пропустить элементы ошибки, попробуйте внутри:

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

В любом другом случае я бы предпочел попробовать снаружи. Код более читабелен, он более чистый. Может быть, было бы лучше, если вместо возврата NULL выдается исключение IllegalArgumentException в случае ошибки

1 голос
/ 05 октября 2008

Я хотел бы добавить свои 0.02c о двух конкурирующих соображениях, когда смотрю на общую проблему, где расположить обработку исключений:

  1. Чем шире ответственность блока try-catch (т.е. за пределами цикла в вашем случае) означает, что при изменении кода на более позднем этапе вы можете по ошибке добавить строку, которая обрабатывается вашим существующим catch блок; возможно непреднамеренно. В вашем случае это менее вероятно, потому что вы явно ловите NumberFormatException

  2. Чем «меньше» ответственность блока try-catch, тем сложнее становится рефакторинг. Особенно, когда (как в вашем случае) вы выполняете «нелокальную» инструкцию из блока catch (оператор return null).

1 голос
/ 01 октября 2008

положи внутрь. Вы можете продолжить обработку (если хотите) или можете вызвать полезное исключение, которое сообщает клиенту значение myString и индекс массива, содержащего неверное значение. Я думаю, что NumberFormatException уже скажет вам плохое значение, но принцип заключается в том, чтобы поместить все полезные данные в исключения, которые вы выбрасываете. Подумайте о том, что вам будет интересно в отладчике на данном этапе программы.

Рассмотрим:

try {
   // parse
} catch (NumberFormatException nfe){
   throw new RuntimeException("Could not parse as a Float: [" + myString + 
                              "] found at index: " + i, nfe);
} 

В случае необходимости вы по-настоящему оцените подобное исключение, содержащее как можно больше информации.

...