無関係な質問 を尋ねましたが、私は次のようなコードを持っていました:
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
// Check property values
}
これは最適ではなく、代わりに(私が正しく理解していれば)これを行うべきだと主張するコメントを受け取りました。
public boolean equals(Object obj)
{
if (this == obj)
return true;
else if (obj == null)
return false;
else if (getClass() != obj.getClass())
return false;
// Check property values
}
Returnステートメントのため、なぜそれらのどれが他よりも効率的または高速である必要があるのか、実際にはわかりません。特定のオブジェクトが与えられた場合、両方のメソッドは、私の知る限り、同じ数のチェックを実行する必要があります。また、returnステートメントがあるため、追加のコードが実行されることはありません。
ここで何か不足していますか?何かありますか?コンパイラの最適化や何かが起こっているのでしょうか?
これはマイクロ最適化であることを知っており、同じ位置にあるすべてのifでクリーンに見えるので、どちらかの方法で最初に固執する可能性があります。しかし、私はそれを助けることはできません。気になる!
生成されたバイトコードはこれら2つのケースで同一であるため、純粋にスタイルの問題です。
2つのメソッドを作成しましたe1
およびe2
と両方がこのバイトコードを生成しました(javap -v
):
public boolean e1(Java.lang.Object); コード: Stack = 2、Locals = 2、Args_size = 2 0:aload_0 1:aload_1 2:if_acmpne 7 5:iconst_1 6:ireturn 7:aload_1 8:ifnonnull 13 11:iconst_0 12:ireturn 13:aload_0 14:invokevirtual#25; //メソッドJava/lang/Object.getClass :()Ljava/lang/Class; 17:aload_1 18:invokevirtual#25; //メソッドJava/lang/Object.getClass :()Ljava/lang/Class; 21:if_acmpeq 26 24:iconst_0 25:ireturn
コンパイルするために、その後に置いたコードを省略しました。
どちらも他より効率的ではありません。コンパイラは2つが同一であることを簡単に確認できます。実際、Suns/Oracles javac
は2つのメソッドに対して同一のバイトコードを生成します。
これがIfTest
クラスです:
class IfTest {
public boolean eq1(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
return true;
}
public boolean eq2(Object obj) {
if (this == obj)
return true;
else if (obj == null)
return false;
else if (getClass() != obj.getClass())
return false;
return true;
}
}
javac
を使用してコンパイルしました。逆アセンブリは次のとおりです。
public boolean eq1(Java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 7
5: iconst_1
6: ireturn
7: aload_1
8: ifnonnull 13
11: iconst_0
12: ireturn
13: aload_0
14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
17: aload_1
18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
21: if_acmpeq 26
24: iconst_0
25: ireturn
26: iconst_1
27: ireturn
public boolean eq2(Java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 7
5: iconst_1
6: ireturn
7: aload_1
8: ifnonnull 13
11: iconst_0
12: ireturn
13: aload_0
14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
17: aload_1
18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class;
21: if_acmpeq 26
24: iconst_0
25: ireturn
26: iconst_1
27: ireturn
つまり、最初のバージョン(else
なし)を使用することをお勧めします。一部の人々は、elseの部分がよりクリーンwithであると主張するかもしれませんが、私は反対を主張します。 含むelse
は、プログラマがそれが不要であることを認識していなかったことを示します。
これらの実装の1つを他の実装に置き換える実際的な理由はありません。
2番目の例は、1つのメソッドで複数のreturnステートメントを回避したい場合に意味があります。そのようなコーディングを好む人もいます。 Then if-else if構文が必要です:
public boolean equals(Object obj)
{
boolean result = true;
if (this == obj)
result = true;
else if (obj == null)
result = false;
else if (getClass() != obj.getClass())
result = false;
return result;
}
このように考えてください。 returnステートメントが実行されると、コントロールがメソッドを離れるので、読みやすさを追加すると主張したくない限り、else
は実際には値を追加しません(実際にはそうは思わないが、他の同意しない場合があります)。
だからあなたが持っているとき:
if (someCondition)
return 42;
if (anotherCondition)
return 43;
2番目のelse
にif
を追加しても、実際には値はありません。
実際、私は Resharper と呼ばれるC#コードを記述するときにツールを使用します。このような状況では、実際にelse
は役に立たないコードとしてマークされます。したがって、一般的には、それらを省略した方がよいと思います。ヨアヒムがすでに述べたように、コンパイラーはとにかくそれらを最適化します。
私はこのコードを少し改善できると思います(気を付けてくださいvery可読です):
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
instanceof
演算子は、これらの両方を組み合わせたものと同等であり、おそらくより高速です-コードが少なく、メソッドの呼び出しはありません。
if (!(obj instanceof MyClass))
return false;
しかし、私は何を知っていますか...バイトコードを分析するのが面倒です(これまでに行ったことがない)。 :-p