У меня есть цикл, который выглядит примерно так:
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, возможно, внутри собственного метода. Тем не менее, есть еще споры о том, что быстрее. Может кто-нибудь проверить это и вернуться с единым ответом?
java
performance
loops
try-catch
Майкл Майерс
источник
источник
Ответы:
ПРОИЗВОДИТЕЛЬНОСТЬ:
В том, где размещены структуры try / catch, нет абсолютно никакой разницы в производительности. Внутренне они реализованы в виде таблицы диапазона кода в структуре, которая создается при вызове метода. Пока метод выполняется, структуры try / catch полностью не видны, если только не произойдет выброс, тогда место ошибки сравнивается с таблицей.
Вот ссылка: http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html
Таблица описана примерно на полпути вниз.
источник
Производительность : как сказал Джеффри в своем ответе, в Java это не имеет большого значения.
Как правило , для удобочитаемости кода выбор того, где перехватить исключение, зависит от того, хотите ли вы, чтобы цикл продолжал обработку или нет.
В вашем примере вы вернулись после ловли исключения. В этом случае я бы поставил try / catch вокруг цикла. Если вы просто хотите поймать плохое значение, но продолжите обработку, поместите его внутрь.
Третий способ : вы всегда можете написать свой собственный статический метод ParseFloat и иметь дело с обработкой исключений в этом методе, а не в цикле. Делаем обработку исключений изолированной от самого цикла!
источник
Хорошо, после того, как Джеффри Л. Уитледж сказал, что не было никакой разницы в производительности (по состоянию на 1997 год), я пошел и проверил ее. Я провел этот небольшой тест:
Я проверил полученный байт-код, используя javap, чтобы убедиться, что ничего не вставлено.
Результаты показали, что, предполагая незначительную оптимизацию JIT, Джеффри прав ; нет абсолютно никакой разницы в производительности на Java 6, клиентской виртуальной машине Sun (у меня не было доступа к другим версиям). Общая разница во времени составляет порядка нескольких миллисекунд за весь тест.
Поэтому единственным соображением является то, что выглядит наиболее чистым. Я считаю, что второй путь уродлив, поэтому я буду придерживаться либо первого, либо пути Рэя Хейса .
источник
Хотя производительность может быть одинаковой, а то, что «выглядит» лучше, очень субъективно, функциональность по-прежнему довольно велика. Возьмите следующий пример:
Цикл while находится внутри блока try catch, переменная 'j' увеличивается до 40, выводится, когда j mod 4 равно нулю, и выдается исключение, когда j достигает 20.
Перед любыми подробностями, вот другой пример:
Та же логика, что и выше, с той лишь разницей, что блок try / catch теперь находится внутри цикла while.
Здесь выводится (в то время как в try / catch):
И другой вывод (попробуйте / поймайте в то время как):
Там у вас есть довольно существенная разница:
в то время как в try / catch вырывается из цикла
попробуй / поймай пока держит цикл активным
источник
try{} catch() {}
цикл, если цикл рассматривается как один «блок», и обнаружение проблемы в этом блоке заставляет весь «блок» (или цикл в данном случае) идти на юг. Поместитеtry{} catch() {}
в цикл, если вы рассматриваете одну итерацию цикла или один элемент в массиве как целую «единицу». Если возникает исключение в этом «модуле», мы просто пропускаем этот элемент и затем переходим к следующей итерации цикла.Я согласен со всеми постами производительности и читабельности. Однако есть случаи, когда это действительно имеет значение. Несколько других людей упомянули об этом, но это может быть легче увидеть на примерах.
Рассмотрим этот слегка измененный пример:
Если вы хотите, чтобы метод parseAll () возвращал значение null, если есть какие-либо ошибки (как в оригинальном примере), вы бы поместили try / catch снаружи следующим образом:
На самом деле, вам, вероятно, следует возвращать здесь ошибку, а не ноль, и, как правило, мне не нравится иметь несколько возвращений, но вы поняли идею.
С другой стороны, если вы хотите, чтобы он просто игнорировал проблемы и разбирал все возможные строки, вы бы поместили try / catch внутри цикла следующим образом:
источник
Как уже упоминалось, производительность одинакова. Однако пользовательский опыт не обязательно идентичен. В первом случае вы быстро потерпите неудачу (то есть после первой ошибки), однако, если вы поместите блок try / catch в цикл, вы можете зафиксировать все ошибки, которые будут созданы для данного вызова метода. При разборе массива значений из строк, где вы ожидаете ошибок форматирования, определенно есть случаи, когда вы хотели бы иметь возможность представить все ошибки пользователю, чтобы им не нужно было пытаться исправить их по одному ,
источник
Если это не все или ничего, то первый формат имеет смысл. Если вы хотите иметь возможность обрабатывать / возвращать все исправные элементы, вам нужно использовать вторую форму. Это были бы мои основные критерии для выбора между методами. Лично, если бы это было все или ничего, я бы не использовал вторую форму.
источник
Пока вы знаете, что вам нужно сделать в цикле, вы можете поместить try try вне цикла. Но важно понимать, что цикл закончится, как только произойдет исключение, и это не всегда может быть тем, что вы хотите. На самом деле это очень распространенная ошибка в программном обеспечении на основе Java. Людям необходимо обработать несколько элементов, таких как очистка очереди, и ложно полагаться на внешний оператор try / catch, обрабатывающий все возможные исключения. Они также могут обрабатывать только определенное исключение внутри цикла и не ожидать появления какого-либо другого исключения. Затем, если возникает исключение, которое не обрабатывается внутри цикла, тогда цикл будет «предопределенным», он может закончиться преждевременно, и внешний оператор catch обработает исключение.
Если в жизни цикл выполнял свою роль по освобождению очереди, то этот цикл, скорее всего, мог бы закончиться до того, как эта очередь была действительно очищена. Очень распространенная ошибка.
источник
В ваших примерах нет функциональной разницы. Я считаю ваш первый пример более читабельным.
источник
Вы должны предпочесть внешнюю версию над внутренней версией. Это всего лишь конкретная версия правила, переместите все, что находится за пределами цикла, и вы можете выйти за пределы цикла. В зависимости от компилятора IL и компилятора JIT две ваши версии могут иметь или не иметь разные характеристики производительности.
С другой стороны, вам, вероятно, стоит взглянуть на float.TryParse или Convert.ToFloat.
источник
Если вы поместите try / catch внутри цикла, вы будете продолжать цикл после исключения. Если вы поместите его вне цикла, вы остановитесь, как только будет сгенерировано исключение.
источник
С моей точки зрения, блоки try / catch необходимы для обеспечения правильной обработки исключений, но создание таких блоков влияет на производительность. Поскольку циклы содержат интенсивные повторяющиеся вычисления, не рекомендуется помещать блоки try / catch внутри циклов. Кроме того, кажется, что, где возникает это условие, часто это «Исключение» или «RuntimeException», который перехватывается. Следует избегать попадания RuntimeException в код. Опять же, если вы работаете в большой компании, важно правильно зарегистрировать это исключение или прекратить возникновение исключения во время выполнения. Весь смысл этого описания
PLEASE AVOID USING TRY-CATCH BLOCKS IN LOOPS
источник
установка специального фрейма стека для try / catch добавляет дополнительные издержки, но JVM может обнаружить факт возврата и оптимизировать его.
в зависимости от количества итераций разница в производительности, вероятно, будет незначительной.
Однако я согласен с остальными, что наличие петли вне ее делает корпус петли более чистым.
Если есть вероятность, что вы когда-нибудь захотите продолжить обработку, а не завершить работу, если найдется недопустимый номер, тогда вы бы хотели, чтобы код был внутри цикла.
источник
Если он внутри, то вы получите накладные расходы структуры try / catch N раз, а не один раз снаружи.
Каждый раз, когда вызывается структура Try / Catch, это увеличивает накладные расходы на выполнение метода. Просто немного памяти и тиков процессора, необходимых для работы со структурой. Если вы выполняете цикл 100 раз и, предположительно, скажем, стоимость составляет 1 тик за вызов try / catch, то наличие Try / Catch внутри цикла обойдется вам в 100 тиков, в отличие от 1 тика, если это вне петли.
источник
Весь смысл исключений состоит в том, чтобы поощрять первый стиль: позволить обработке ошибок консолидироваться и обрабатываться один раз, а не сразу на каждом возможном месте ошибки.
источник
положить его внутрь. Вы можете продолжить обработку (если хотите) или можете выдать полезное исключение, которое сообщает клиенту значение myString и индекс массива, содержащего неверное значение. Я думаю, что NumberFormatException уже скажет вам плохое значение, но принцип заключается в том, чтобы поместить все полезные данные в исключения, которые вы выбрасываете. Подумайте о том, что было бы вам интересно в отладчике на данном этапе программы.
Рассматривать:
В трудное время вы по достоинству оцените подобное исключение, содержащее как можно больше информации.
источник
Я хотел бы добавить свои собственные
0.02c
соображения о двух конкурирующих соображениях, когда смотрю на общую проблему того, где расположить обработку исключений:Чем «шире» ответственность
try-catch
блока (т. Е. Вне цикла в вашем случае) означает, что при изменении кода на более позднем этапе вы можете по ошибке добавить строку, которая обрабатывается вашим существующимcatch
блоком; возможно непреднамеренно. В вашем случае это менее вероятно, потому что вы явно ловитеNumberFormatException
Чем «меньше» ответственность
try-catch
блока, тем сложнее становится рефакторинг. Особенно когда (как в вашем случае) вы выполняете «нелокальную» инструкцию изcatch
блока (return null
оператора).источник
Это зависит от обработки ошибок. Если вы просто хотите пропустить элементы ошибки, попробуйте внутри:
В любом другом случае я бы предпочел попробовать снаружи. Код более читабелен, он более чистый. Может быть, было бы лучше, если бы в случае ошибки было возвращено исключение IllegalArgumentException в случае ошибки.
источник
Я положу свои 0,02 доллара. Иногда вам приходится добавлять «наконец» позже в ваш код (потому что, кто когда-либо отлично пишет свой код в первый раз?). В этих случаях внезапно становится более целесообразным использовать try / catch вне цикла. Например:
Потому что, если вы получили ошибку или нет, вы хотите разорвать соединение с базой данных (или выбрать свой любимый тип другого ресурса ...) один раз.
источник
Другим аспектом, не упомянутым выше, является тот факт, что каждый try-catch оказывает некоторое влияние на стек, что может иметь значение для рекурсивных методов.
Если метод "external ()" вызывает метод "inner ()" (который может вызывать себя рекурсивно), попробуйте найти try-catch в методе "outer ()", если это возможно. Простой пример «сбоя стека», который мы используем в классе производительности, дает сбой примерно в 6400 кадрах, когда try-catch находится во внутреннем методе, и примерно в 11 600, когда он находится во внешнем методе.
В реальном мире это может быть проблемой, если вы используете шаблон Composite и имеете большие сложные вложенные структуры.
источник
Если вы хотите перехватывать исключения для каждой итерации или проверять, на какой итерации выдается исключение, и перехватывать все исключения в итерациях, поместите try ... catch внутри цикла. Это не разорвет цикл, если возникнет исключение, и вы можете перехватить каждое исключение в каждой итерации на протяжении всего цикла.
Если вы хотите разорвать цикл и проверять Исключение при каждом вызове, используйте try ... catch вне цикла. Это нарушит цикл и выполнит операторы после catch (если есть).
Все зависит от ваших потребностей. Я предпочитаю использовать try ... catch внутри цикла при развертывании, так как, если возникает исключение, результаты не являются неоднозначными, и цикл не будет прерываться и выполняться полностью.
источник