どちらが速いですか:
これのどちらか
try {
n.foo();
}
catch(NullPointerException ex) {
}
または
if (n != null) n.foo();
どちらが速いかという問題ではなく、正確さの問題です。
例外は、まさにそれである状況、例外です。
通常のビジネスロジックの一部としてn
がnull
である可能性がある場合は、if..else
を使用し、そうでない場合はthrow
を例外とします。
if (n != null) n.foo();
より高速です。
Nullポインターを明示的にテストする方が、例外処理を使用するよりもはるかに高速です。
記録として、例外を使用する際のほとんどのほとんどは、例外オブジェクトのインスタンス化で発生します。特にfillInStackTrace()
の呼び出しでは、次のことを行う必要があります。
場合によっては、例外オブジェクトを再利用するか、アプリケーション固有の例外のfillInStackTrace()
メソッドをオーバーライドして操作を行わないようにすることで、これを減らすことができます。どちらの場合も、予期しない例外のデバッグに役立つ適切なスタックトレースが使用できなくなるという欠点があります。 (そして、これらはどちらもOPの例には当てはまりません。)
例外のインスタンス化にはコストがかかりますが、例外のスロー、伝播、およびキャッチも必ずしも安価ではありません。
明示的なヌルテストがより良い考えである2番目の理由があります。このことを考慮:
_try {
doSomething(a.field);
} catch (NullPointerException ex) {
System.err.println("a.field is null");
}
_
_a.field
_式の評価中ではなく、doSomething(...)
の呼び出し内でNPEが発生した場合はどうなりますか?確かに、NPEをキャッチしますが、それを誤診して続行しようとします... _a.field
_が設定されていないか何かであると誤って想定します。
「予期される」NPEと「予期しない」NPEを区別することは理論的には可能ですが、実際には非常に困難です。はるかに単純で堅牢なアプローチは、期待するnull
値を明示的にテストし(たとえば、if
ステートメントを使用)、すべてのNPEをバグとして扱うことです。
(これが@Mitchが「例外を例外として扱う」という意味であると確信していますが、説明的な例で物事を詳しく説明するのに役立つと思います...)
これに対する答えは、見た目ほど単純ではありません。これは、オブジェクトが実際にnullである回数の割合に依存するためです。これが非常にまれな場合(たとえば、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を使用すると、違いはほとんど目立ちません。それでも:本当に例外でない限り、NullPointerExceptionのキャッチは使用したくありません。
n.foo()
がたまたま内部でNPEをスローした場合、長いデバッグセッションがオフになります(さらに悪いことに、アプリが本番環境で失敗します)。ただそれをしないでください。
とにかく、何ナノ秒節約する予定ですか?
Java Specialist's Newsletter :)を読んでいるのは私だけではないことに気づきました。
セマンティックの違い(NPEは必ずしもn
の逆参照によって引き起こされるわけではありませんが、foo()
のエラーによってスローされた可能性があります)、および-があるという事実は別として読みやすさの問題(try/catchはif
よりも読者を混乱させます)、n != null
の場合はほぼ同じくらい高速である必要があります(if/elseバージョンにはわずかな利点があります) 、ただし、n == null
if/elseの方がはるかに高速です。どうして?
n == null
の場合、VMは新しい例外オブジェクトを作成し、そのスタックトレースを入力する必要があります。スタックトレース情報の取得には非常にコストがかかるため、ここでtry/catchバージョンははるかに高価です。if
を避けることで、n != null
のときに安くなると考えています。ただし、VMは逆参照時に暗黙のnullチェックを実行します...つまり、JITがn
が非-でなければならないと判断できない限り、 null、if/elseバージョンで可能です。これは、if/elseバージョンとtry/catchバージョンがほぼ同じように実行される必要があることを意味します。しかし...良い答えのほかに(例外的な場合には例外を使用してください)、基本的にどこでもnullチェックを回避しようとしているようです。 Java 7には、NPEをスローする代わりにn?.foo()
が呼び出されたときにnullを返す「nullsafe」演算子があります。これはGroovy言語から借用しています。トレンドもあります。本当に必要な場合(つまり、ライブラリの処理)を除いて、コードでnullを完全に使用しないようにするため。これに関する詳細については、この他の回答を参照してください。 != nullステートメントの回避
ifコンストラクトの方が高速です。条件は、マシンコード(プロセッサ命令)に簡単に変換できます。
別の方法(try-catch)では、NullPointerExceptionオブジェクトを作成する必要があります。
通常、例外の処理にはコストがかかります。 VM Spec は、いくらかについての洞察を与えるかもしれませんが、上記の場合、if (n != null) n.foo();
の方が高速です。
私はミッチウィートに同意しますが、本当の質問は正しさです。
@Mitch Wheat-彼の弁護では、これはかなり不自然な例です。 :)
間違いなく2番目の形式ははるかに高速です。 _try-catch
_シナリオでは、何らかの形式のnew Exception()
を実行するexception
をスローします。次に、メソッド呼び出しであるcatch
ブロックが呼び出され、その中にあるコードを実行する必要があります。あなたはアイデアを得る。
この問題は、ハインツ博士によって最近議論されました。
http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov
他のポスターによって指摘された多くの理由から、最初にif .. then ..elseの方が優れています。
しかし、それは必然的に速くはありません!これは、nullオブジェクトとnullオブジェクトではないオブジェクトの比率に大きく依存します。 nullをテストするのではなく、例外を処理するのにおそらく数十万倍のリソースが必要ですが、nullオブジェクトが100万オブジェクトごとに1回だけ発生する場合、例外オプションはわずかに高速になります。しかし、プログラムを読みにくくし、デバッグを難しくする価値があるほど速くはありません。
try-catchブロックは例外スタックトレースを発生させるため、if-elseの方が高速です。 If-Elseブロックが評価を行うために1つの命令を実行していると見なすことができますが、Try-Catchは何千もの命令を実行して、例外が発生したときに例外を発生させます。