2010-12-14 16 views
2

今日、私は次のようつまずいてきました:Javaはここで私の "equals"実装と何をしていますか?

NewClass:

@Override 
public boolean equals(Object obj) { 
    return false; 
} 

public boolean equals(NewClass obj) { 
    return value == obj.getValue(); 
} 

NewClass1:

は以下の-methods "に等しい" を持っている二つのクラスNewClassとNewClass1を、考えてみましょう

@Override 
public boolean equals(Object obj) { 
    if(!(obj instanceof NewClass1)) { 
     return false; 
    } 
    return equals((NewClass1) obj); 
} 

public boolean equals(NewClass1 obj) { 
    return value == obj.getValue(); 
} 

私が気付くのは、NewClass1のequalsがNewClassのequalsよりも指数関数的に遅いようです10.000.000は3000msに対して14msを呼び出す)。最初は、これは "instanceof"チェックに関連していると思っていましたが、 "return equals((NewClass1)obj);" "return false;"と返すNewClass1では、突然それは多かれ少なかれ同じ速さで実行されます。私の意見では、equals(Object)のreturn文は実際には決して呼び出されるべきではないので、ここで何が起きているのかは分かりません。私はここで何が間違っていますか?通常と呼ばれることはありませんでしょう最初の例equals(NewClass)

public static void main(String[] args) { 
    // TODO code application logic here 

    NewClass i1 = new NewClass(1); 
    NewClass i2 = new NewClass(1); 
    NewClass i3 = new NewClass(5); 

    NewClass1 j1 = new NewClass1(1); 
    NewClass1 j2 = new NewClass1(1); 
    NewClass1 j3 = new NewClass1(5); 

    Object o1 = new Object(); 
    Object o2 = new Object(); 


    assert(i1.equals(i1)); 
    assert(i1.equals(i2)); 
    assert(i1.equals(i3) == false); 
    assert(i1.equals(o1) == false); 

    assert(j1.equals(j1)); 
    assert(j1.equals(j2)); 
    assert(j1.equals(j3) == false); 
    assert(j1.equals(o1) == false); 


    long start = System.currentTimeMillis(); 

    for(int i=0; i<1000000000; i++) { 
     i1.equals(i1); 
     i1.equals(i2); 
     i1.equals(o1); 
     i1.equals(o2); 
    } 

    long end = System.currentTimeMillis(); 

    System.out.println("Execution time was "+(end-start)+" ms."); 



    start = System.currentTimeMillis(); 

    for(int i=0; i<1000000000; i++) { 
     j1.equals(j1); 
     j1.equals(j2); 
     j1.equals(o1); 
     j1.equals(o2); 
    } 

    end = System.currentTimeMillis(); 

    System.out.println("Execution time was "+(end-start)+" ms."); 
} 
+2

'instanceof'が呼び出されないように' return false; 'に置き換えるとコードが最適化されるのでしょうか? – thejh

+1

@thejh:それは実際には意味をなさないでしょう...これは答えだったはずです。最初の有用なコメントです。私はそれをチェックします。 – fresskoma

+1

最初の例にバグがあることに気付きましたら幸いです。使用するequals()の特定の実装は、コンパイル時に選択されます。これは、オブジェクトのタイプがコンパイル時に知られていない場合、例えば、実際に 'NewClass'インスタンスを参照する' Object'があれば、常にfalseを返します。具体的には、 'n.equals((Object)n)'はfalseです。ここでnは 'NewClass'のインスタンスです。 – JeremyP

答えて

3

私はそれが時間を消費しているインスタンスのテストだと思います。そのメソッドの最終的な戻り値を常にfalseに戻すように変更すると、コンパイラはおそらく評価に関係なく結果が同じ(falseを返す)ため、条件付きを削除します。これはまた、最終的なリターンの変更がコードパスで決して実際には到達すべきではないと言っているので、何にも何の影響も及ぼさない理由を説明します。

より一般的に言えば、コードの変更は、コンパイラがコードを最適化する方法を変更することによって、実行されたコードパスにない場合でもパフォーマンスに影響を与える可能性があります。

+0

明らかに、コードが削除され、 "instanceof"は本当に遅い部分でした:)もう一つのJavaの謎が解決されました^^ – fresskoma

2

以下は、私はそこにいくつかのミスを犯した場合には、私の「ベンチマークコード」、です。 equals(Object)はHotSpot(または同様のもの)によってインライン化することができ、テストの本体を効果的に無力化することができます。

エンベロープの計算は、参考にすることができます。 「10.000.000コール8ms」は1,250,000,000回の繰り返しです。 4GHzのプロセッサを仮定すると、それは反復ごとに約3サイクルです。何か価値のあることをするために少し速い。実際、このコードでは10,000,000ではなく1,000,000,000と言います。

実際、実際のコードでは、すべてのループ本体を削除することができました。それで、あなたが何を測定しているかは本当に重要ではありません。何か役に立つものは信頼できるものではありません。マイクロベンチマークを行うことには他にも多くの問題があり、他の多くの場所で読むことができます。

+1

等号(NewClass)を呼び出す必要があります。そうでなければ常にfalseを返します。私のベンチマークコードでのアサート)。 – fresskoma

+0

また、私はベンチマーク時間が間違っていますが、それは8msではなく14msであった。 – fresskoma

+0

@ x3roああ、私はあなたが実際に過負荷を使用しているのを見ます。 14ミリ秒と8ミリ秒は本当に重要ではありません。サイクル数はごくわずかです(記述に正しい繰り返し回数があると仮定すると、コードは反復ごとにサイクルのごく一部を示しています)。 –

1

最初の例では、常にfalseを返します。これは非常に高速です。 2番目の例では、はるかに長い比較アルゴリズムを持っています

+0

私はそれを知っています。しかし、私の質問をもう一度読むと、私は実際には混乱していることに気づくでしょうが、ここではinstanceof呼び出しが違いをもたらすようには見えませんが、JVMがNewClass1でパラメータ化された他のequalsメソッドを呼び出します。 – fresskoma

0

まあ、最初の例はほとんど何もしません。反復回数を100000に減らして、同じ結果、5または6ミリ秒を得ることができます。つまり、JVMはコードの一部を積極的に最適化します。

関連する問題