Что быстрее, попробуйте поймать или if-else в java (производительность WRT)

Какой из них быстрее:

Либо это

try {
  n.foo();
} 
catch(NullPointerException ex) {
}

или

if (n != null) n.foo();

Ответ 1

Это не вопрос, который быстрее, а скорее правильный.

Исключение составляют обстоятельства, которые в точности являются исключительными.

Если возможно, что n будет null частью обычной бизнес-логики, используйте исключение if..else, else throw.

Ответ 2

if (n != null) n.foo();

быстрее.

Ответ 3

Явное тестирование для нулевого указателя намного быстрее, чем использование обработки исключений.

Для записи большинство исключений в использовании исключений возникают в экземпляре объекта исключения. В частности, при вызове fillInStackTrace(), который должен:

  • проверить каждый стек стека для текущего стека потоков и
  • создать структуру данных для захвата деталей фрейма стека.

В некоторых случаях вы можете уменьшить это путем повторного использования объекта исключения или переопределения специального метода исключения fillInStackTrace() для конкретного приложения, чтобы сделать его незанятым. Недостатком в обоих случаях является то, что правильные стеки стека больше не будут доступны, чтобы помочь вам отлаживать неожиданные исключения. (И ни одно из них не применимо к примеру OP.)

Несмотря на то, что создание экземпляров исключений является дорогостоящим, исключение, распространение и улавливание не совсем дешевы.


Существует вторая причина, по которой явное тестирование нулей является лучшей идеей. Рассмотрим это:

try {
    doSomething(a.field);
} catch (NullPointerException ex) {
    System.err.println("a.field is null");
}

Что произойдет, если NPE произойдет в вызове doSomething(...) вместо оценки выражения a.field? Конечно, мы поймаем NPE, но мы его неправильно определим, а затем попытаемся продолжить... неправильно полагая, что a.field не работает или что-то в этом роде.

Отличие "ожидаемого" NPE от "неожиданного" NPE теоретически возможно, но на практике очень сложно. Более простой и надежный подход заключается в явной проверке ожидаемых значений null (например, с помощью оператора if) и обработки всех NPE как ошибок.

(Я уверен, что это то, что @Mitch означает "обрабатывать исключения как исключительные", но я думаю, что это помогает описать ситуацию наглядным примером...)

Ответ 4

Ответ на этот вопрос не так прост, как кажется, потому что это будет зависеть от процента времени, в течение которого объект действительно равен нулю. Когда это очень редко (скажем, в 0,1% случаев), это может быть даже быстрее. Чтобы протестировать это, я провел некоторые сравнительные тесты со следующими результатами (с клиентом Java 1.6):

Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.45 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.46 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.42 seconds
Average time of NullExceptionTest: 0.52 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.41 seconds
Average time of NullExceptionTest: 1.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.07 seconds
Average time of NullExceptionTest: 7.48 seconds

Это кажется мне довольно убедительным. NPE очень медленные. (Я могу опубликовать код бенчмаркинга, если он захочет)

изменить: Я только что сделал интересное открытие: при бенчмаркинге с использованием JVM сервера результаты резко меняются:

Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.33 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.32 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.31 seconds
Average time of NullExceptionTest: 0.32 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.28 seconds
Average time of NullExceptionTest: 0.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.05 seconds
Average time of NullExceptionTest: 0.04 seconds

Используя VM сервера, разница вряд ли заметна. Тем не менее: я бы предпочел не использовать catching NullPointerException, если это действительно исключение.

Ответ 5

Если n.foo() происходит внутренне сбрасывается NPE, вы отключены для длинной сессии отладки (или, что еще хуже, ваше приложение выходит из строя). Только не делай этого.

Сколько nano-секунд вы планируете сохранить, так или иначе?

Ответ 6

Я замечаю, что я не единственный, кто читает Информационный бюллетень Java Specialist:)

Помимо того факта, что существует семантическая разница (NPE не обязательно вызван разыменованием n, это может быть вызвано некоторой ошибкой в ​​foo()), а < сильная проблема с читабельностью (попытка/catch более запутанна для читателя, чем if), они должны быть примерно одинаковыми в случае, когда n != null (с версией if/else, имеющей небольшое преимущество), но когда n == null, если /else намного быстрее. Почему?

  • Когда n == null, виртуальная машина должна создать новый объект исключения и заполнить трассировку стека. Информация о трассировке стека очень дорогая, поэтому версия try/catch намного дороже.
  • Некоторые считают, что условные утверждения медленнее, потому что они предотвращают конвейерную обработку инструкций, и, избегая явного if, они считают, что они ушли дешево, когда n != null. Дело в том, что VM будет выполнять неявную проверку нуля при разыменовании... то есть, если JIT не может определить, что n должен быть не нулевым, что он может в if/else. Это означает, что версии if/else и try/catch должны выполняться примерно одинаково. Но...
  • ... предложения try/catch могут мешать тому, как JIT может встроить вызов метода, что означает, что он не сможет оптимизировать версию try/catch, а также if/else.

Ответ 7

Помимо хороших ответов (используйте исключения для исключительных случаев), я вижу, что вы в основном пытаетесь избежать нулевых проверок везде. Java 7 будет иметь "нулевой безопасный" оператор, который будет возвращать значение null при вызове n?.foo() вместо того, чтобы бросать NPE. Это заимствовано из языка Groovy. Также существует тенденция избегать использования нулевого значения в одном коде, кроме случаев, когда это действительно необходимо (то есть: обращение к библиотекам). См. Другой ответ, чтобы узнать больше об этом. Избежать!= нулевые утверждения

Ответ 8

Как правило, дорого обрабатывать исключения. VM Spec может дать вам некоторое представление о том, сколько, но в вышеприведенном случае if (n != null) n.foo(); выполняется быстрее.

Хотя я согласен с Митчем Пшеницей относительно настоящего вопроса, это правильность.

@Mitch Wheat - В его защиту это довольно надуманный пример.:)

Ответ 9

Конструкция if выполняется быстрее. Условие можно легко перевести на машинный код (инструкции процессора).

Альтернативный (try-catch) требует создания объекта NullPointerException.

Ответ 10

Определенно вторая форма намного быстрее. В сценарии try-catch он выдает exception, который выполняет new Exception() какой-либо формы. Затем вызывается блок catch, который является вызовом метода и должен выполнять любой код в нем. Вы получаете идею.

Ответ 11

Во-первых, если.. то.. еще лучше, по многочисленным причинам, указанным другими плакатами.

Однако это не обязательно быстрее! Это зависит от отношения нулевых объектов к непустым объектам. Это, вероятно, требует в сотни тысяч раз ресурсов для обработки исключения, а не для проверки на нуль, однако, если нулевой объект возникает только один раз для каждых миллионов объектов, то опция исключения будет немного быстрее. Но не так быстро, что его ценность делает вашу программу менее читаемой и сложной для отладки.

Ответ 13

if-else быстрее, потому что блок try-catch повышает трассировку стека исключений. Вы можете принять это, поскольку блок If-Else выполняет одну команду для проведения оценки, но Try-Catch будет запускать тысячи инструкций для повышения исключения, когда это произойдет.