そこにパフォーマンスを確認する2つの方法を書いた
public class Test1 {
private String value;
public void notNull(){
if( value != null) {
//do something
}
}
public void nullNot(){
if( null != value) {
//do something
}
}
}
コンパイル後にバイトコードであることを確認しました
public void notNull();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: getfield #2; //Field value:Ljava/lang/String;
4: ifnull 7
7: return
LineNumberTable:
line 6: 0
line 9: 7
StackMapTable: number_of_entries = 1
frame_type = 7 /* same */
public void nullNot();
Code:
Stack=2, Locals=1, Args_size=1
0: aconst_null
1: aload_0
2: getfield #2; //Field value:Ljava/lang/String;
5: if_acmpeq 8
8: return
LineNumberTable:
line 12: 0
line 15: 8
StackMapTable: number_of_entries = 1
frame_type = 8 /* same */
}
ここでは、if条件を実装するために2つのオペコードが使用されます。最初のケースではifnullを使用します-スタックのトップ値がnullであることを確認し、2番目のケースではif_acmpeqを使用します-スタック内の上部の2つの値が等しいか確認します
では、これはパフォーマンスに影響を与えますか? (これは、nullの最初の実装がパフォーマンスと読みやすさの面で優れていることを証明するのに役立ちます:))
生成されたバイトコードの比較は、ほとんどの場合、JITコンパイラーを使用して実行時に最適化されるため、ほとんど意味がありません。この場合、どちらの式も同等に高速であると推測します。違いがある場合は無視できます。
これはあなたが心配する必要があるものではありません。全体像の最適化を探します。
速度(またはメモリ/状況に関係なく)のゲインが無視できる場合は、読みやすさを犠牲にして最適化しないでください。おもう !=null
は一般的に読みやすいので、それを使用してください。
このような質問では、JVMがどれほどスマートになるかを知るのは困難です(答えは「通常、可能であればかなりスマート」であり、この場合は非常に可能であるように見えます)。ただし、念のため、テストしてください。
class Nullcheck {
public static class Fooble { }
Fooble[] foo = {null , new Fooble(), null , null,
new Fooble(), null, null, new Fooble() };
public int testFirst() {
int sum = 0;
for (int i=0 ; i<1000000000 ; i++) if (foo[i&0x7] != null) sum++;
return sum;
}
public int testSecond() {
int sum = 0;
for (int i=0 ; i<1000000000 ; i++) if (null != foo[i&0x7]) sum++;
return sum;
}
public void run() {
long t0 = System.nanoTime();
int s1 = testFirst();
long t1 = System.nanoTime();
int s2 = testSecond();
long t2 = System.nanoTime();
System.out.printf("Difference=%d; %.3f vs. %.3f ns/loop (diff=%.3f)\n",
s2-s1,(t1-t0)*1e-9,(t2-t1)*1e-9,(t0+t2-2*t1)*1e-9);
}
public static void main(String[] args) {
Nullcheck me = new Nullcheck();
for (int i=0 ; i<5 ; i++) me.run();
}
}
そして私のマシンではこれは次の結果をもたらします:
Difference=0; 2.574 vs. 2.583 ns/loop (diff=0.008)
Difference=0; 2.574 vs. 2.573 ns/loop (diff=-0.001)
Difference=0; 1.584 vs. 1.582 ns/loop (diff=-0.003)
Difference=0; 1.582 vs. 1.584 ns/loop (diff=0.002)
Difference=0; 1.582 vs. 1.582 ns/loop (diff=0.000)
だから答えは、いいえ、まったく意味のある違いはありません。 (そして、JITコンパイラーは、同じ数の繰り返し実行の後に、それぞれを高速化するための追加のトリックを見つけることができます。)
Update:上記のコードはアドホックベンチマークを実行します。 [〜#〜] jmh [〜#〜] (存在するようになった!)を使用することは、(いくつかの)マイクロベンチマークの落とし穴を回避するのに役立つ良い方法です。上記のコードは、最悪の落とし穴を回避しますが、明示的なエラー推定値を提供せず、他のさまざまな問題を無視します。最近:JMHを使用してください!また、疑問がある場合は、独自のベンチマークを実行してください。詳細は重要な場合があります。これほど単純なものではそれほど頻繁ではありませんが、それが本当に重要な場合は、管理可能な限り実動に近い状態でチェックインする必要があります。
二項演算子の左側に定数を置くことを好む、Cでの偶発的な割り当てを回避するという苦労して得た知恵とは別に、左側の定数はmoreであることがわかります。最も目立つ位置にある値。
通常、関数本体は数個の変数のみを使用します。通常、どの変数が検査されているかは、コンテキストによって明らかになります。定数を左側に置くことにより、switch
とcase
をより厳密に模倣します。this変数を指定して、一致する値を選択します。左側の値を見ると、選択されている特定の条件に焦点が当てられています。
スキャンするとき
if (var == null)
「ここではvar
を検査しており、...ああ、nullと比較して等しいかどうか比較しています。」と読みました。逆に、スキャンすると
if (null == var)
「値がnullであるかどうかを確認しています。そうです、調査しているのはvar
です。」それはより強い認識です
if (null != var)
私の目はすぐに気づきます。
この直感は、習慣の一貫性、自分が書いたものを読むことを好むこと、そして自分が読んだことを好むことを書くことに由来します。どちらの方法でもそれを学ぶことができますが、変数を左側に置く方がより明確であると他の人がここで答えているため、客観的には真実ではありません。それは、最初に最も明確にしたい表現の側面に依存します。
バイトコードの違いを見るのは魅力的でした。それを共有してくれてありがとう。
違いは無視できるので、最も読みやすい(!= null
imo)
観点からは、パフォーマンスに大きな違いはありません。
ただし、タイプミスエラーをキャッチするには、最初にnullを書き込むと便利です。
たとえば、このコードの記述に慣れている場合:
if (obj == null)
誤って次のように書くことができます:
if (obj = null)
コンパイラーの観点からは、これは問題ありません。
ただし、コードを次のように記述する場合:
if (null == obj)
と書き間違えました:
if (null = obj)
コンパイラーは、その行に誤りがあることを通知します。
読みやすくするために(値!= null)を使用します。ただし、アサーションはいつでも使用できます。
そのような微妙な最適化は、特にJavaのような高水準言語では、コンパイラーの仕事です。
ここでは厳密には関係ありませんが、時期尚早に最適化しないでください!
コーディング中にこの非常に細かい最適化を無視できます
ご覧のとおり、パフォーマンスの違いは非常に小さくなっています。アルゴリズムにさらに焦点を合わせる方が常に良いという細かいことについて心配する必要はありません。そして、明らかに読みやすさが要因です。
最初にnull
を置くと、余分なバイトコードが生成されるように見えますが、それ以外はパフォーマンスに違いがない場合があります。
個人的には、パフォーマンスについて心配する時まで、パフォーマンスについて心配することはありませんでした。
私はnotNull()
アプローチを使用するので、!
を忘れて誤ってnull = value
と入力しても、コンパイラエラーをスローしません。
ああ、究極のパフォーマンスを求める場合は、追加のクラスやメソッドを作成しないでください。 JavaクラスローダーがJITでロードする必要があるため、静的メソッドでも少し時間がかかります。
したがって、変数がnullかどうかを確認する必要がある場合は、次のいずれかでテストするだけです。
if (x == null)
または
if (null == x)
率直に言って、2つのうちの1つを選択するパフォーマンスボーナスは、不要なメソッドを導入するオーバーヘッドによって簡単に相殺されます。
Java-8では、Objects
クラスに2つの追加メソッドが導入されました: Objects#nonNull および Objects#isNull 、これらを使用してnull
チェック。興味深いのは、どちらも最初にオブジェクトを使用することです。
public static boolean isNull(Object obj) {
return obj == null;
}
そして
public static boolean nonNull(Object obj) {
return obj != null;
}
対応して。これが推奨される方法であることを意味します(少なくともコアjdk開発者がそのアプローチを使用しました) Objects source code
「新しい」Java 8機能を使用します。いくつかの例を記述します。
import Java.util.Optional;
public class SillyExample {
public void processWithValidation(final String sampleStringParameter){
final String sampleString = Optional.ofNullable(sampleStringParameter).orElseThrow(() -> new IllegalArgumentException("String must not be null"));
//Do what you want with sampleString
}
public void processIfPressent(final String sampleStringParameter){
Optional.ofNullable(sampleStringParameter).ifPresent(sampleString -> {
//Do what you want with sampleString
});
}
public void processIfPressentWithFilter(final String sampleStringParameter){
Optional.ofNullable(sampleStringParameter).filter("hello"::equalsIgnoreCase).ifPresent(sampleString -> {
//Do what you want with sampleString
});
}
}
を好む null != object
これは、nullチェック専用であることを明確に示しています。