2012-01-20 12 views
4

この質問は、具体的にはさまざまな実装の代替案のパフォーマンスとある程度の簡潔さです。Javaでの等価性のパフォーマンス(instanceOf vs isAssignableFrom)

私は平等の権利を実装する上でthis articleと自分自身をリフレッシュしました。私の質問は特にcanEqualに相当します(同等の関係を保証するため)。

canEqualsメソッドではなく、階層内のすべてのクラスでinstanceOfを使用します(paramenterのインスタンスはコンパイル時クラスです)。 isAssignableFrom(動的に解決される)は、トップレベルのクラスでのみ使用してください。多くの簡潔なコードを作成し、3番目のメソッドをオーバーロードする必要はありません。

ただし、この代替方法が有効です。私が気づく必要のあるパフォーマンス上の考慮事項はありますか?

enum Color { 
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET; 
} 
class Point { 

    int x; 
    int y; 

    public Point(int x, int y) { 
     this.x = x; 
     this.y = y; 
    } 


    @Override public boolean equals(Object other) { 
     boolean result = false; 
     if (other instanceof Point) { 
      Point that = (Point) other; 
      //Option 1 
      //result = (that.canEqual(this) && this.getX() == that.getX() && this.getY() == that.getY()); 
      //Option 2 
      //result = (that.getClass().isAssignableFrom(this.getClass()) && this.getX() == that.getX() && this.getY() == that.getY()); 
      //Option 3 
      //result = (getClass() == that.getClass() && this.getX() == that.getX() && this.getY() == that.getY()); 
     } 
     return result; 
    } 

    @Override public int hashCode() { 
     return (41 * (41 + x) + y); 
    } 

    public boolean canEqual(Object other) { return (other instanceof Point); } 
} 

public class ColoredPoint extends Point{ 
     Color color; 

     public ColoredPoint(int x, int y, Color color) { 
      super(x, y); 
      this.color = color; 
     } 

     @Override public boolean equals(Object other) { 
      boolean result = false; 
      if (other instanceof ColoredPoint) { 
       ColoredPoint that = (ColoredPoint) other; 
       result = (this.color.equals(that.color) && super.equals(that)); 
      } 
      return result; 
     } 

     @Override public int hashCode() { 
      return (41 * super.hashCode() + color.hashCode()); 
     } 

     @Override public boolean canEqual(Object other) { return (other instanceof ColoredPoint); } 

    public static void main(String[] args) { 
     Object p = new Point(1, 2); 
     Object cp = new ColoredPoint(1, 2, Color.INDIGO); 

     Point pAnon = new Point(1, 1) { 
      @Override public int getY() { 
       return 2; 
      } 
     }; 

     Set<Point> coll = new java.util.HashSet<Point>(); 
     coll.add((Point)p); 

     System.out.println(coll.contains(p)); // prints true 
     System.out.println(coll.contains(cp)); // prints false 
     System.out.println(coll.contains(pAnon)); // prints true 
    } 
} 
+0

1つのコメント - このinstanceOfタイプのロジック(通常は)を使用している場合は、通常はequalsメソッドをfinalにする必要があります。 (私はしばしばそれを無視する)。私は答えにさらにコメントをしなければならないかもしれません。 – user949300

+0

ええ、詳しく教えてください。 – smartnut007

+0

私は今あなたが欲しいものについて完全に混乱しています。 PointとColoredPointが同じになることはありますか? ColoredPointはいつでもポイントに等しいことができますか?どちらの答えも問題ありません。しかし、あなたはあなたのアプリケーションのために欲しいですか? – user949300

答えて

3

あなたは、異なる種類のクラスを比較できるようにしたい場合を除き、最も簡単な、最も安全で、最も簡潔で、おそらく最も効率的なIMPLは次のとおりです。

(getClass() == that.getClass()) 
+1

+1。 'that!= null && ... 'を使うと、異なる具体的な型が等しくなるような魅力的な理由がない限り、これは間違いなくあなたのgotoです。 –

+1

-1はい、ただし、 "unless"節は大きな制限事項です。 OPは明らかに異なるタイプのオブジェクト(ポイントとカラーポイント) – user949300

+0

を比較したいと思っています。 @waxwing答えを見てください。 – smartnut007

1

What is the difference between equality and equivalence?ためmy answerを参照してください。

対称性が損なわれるため、異なるクラスの2つのオブジェクトを同一にすることはできません。

編集は:

それは次のようにダウンするかどうかxに来る:

if (other instanceof Point) { 
    Point that = (Point) other; 

    boolean x = that.getClass().isAssignableFrom(this.getClass()); 
} 

getClass() == that.getClass()と同じ力を持っています。 @waxwingさんの回答によるとありません。

正しい場合でも、that.getClass().isAssignableFromに電話すると、パフォーマンス上の利点はありません。

+0

はい、私は決して他には示唆しませんでした。 – smartnut007

+0

@ smartnut007残念ながら(または幸いにも)等価は対称でなければなりません。 Pointのequalsメソッドを使用してColoredPointを別のPointとして扱うこともできますし、まったく違う場合はColoredPointsと等しいかどうかを比較してthis.getClass()== that.getClass()を使用する必要があります。しかし、あなたの "ハイブリッド"アプローチは対称ではないので失敗します。 – user949300

+0

タイプは、サブタイプとの相互同等性を可能にするような方法で等価を定義することができます。そのようなアプローチの1つは、すべてのサブタイプに適用可能な正規形を定義し、2つのオブジェクトが同じ正規形を持つ場合、等価であるとみなすことを指定することです。たとえば、最も一般的なコンクリートサブタイプが2次元配列として行列を格納する抽象的な 'ImmutableMatrix'型を持つこともできますが、定数行列(すべての値が等しい)、対角行列(すべての項目がオフ対角がゼロであることなど) – supercat

0

私はあなたが対称的なOOPS、対称ではないので、ソリューションは失敗すると思います。 See The chapter from Effective Java

Point p = new Point(2,3); 
ColoredPoint cp = new ColoredPoint(2,3, Color.WHITE); 

私は本当の

しかし

cp.equals(p)が偽

です

p.equals(CP)という(あなたのコードを実行していない)と考えています

私はあなたのコードを完全に理解していませんが、コメントアウトされたcanEquals()を参照しています。短い答えは、あなたが色を無視して平等にする必要があるか、または@jthalbornが提案したことをしなければならないということです。

+0

ああ、私を許して、そこにcanEqualへのすべてのコメントがコメントされているはずです。 – smartnut007

+0

あなたは間違っています!上記のサンプルコードのバージョンはp.equals(cp) – smartnut007

+1

のためにtrueを返すことはありませんあなたはコードを編集してそれを変更することができますか? PointがColoredPointに等しくならないようにするには、@ jthalborn answerを使用します。 – user949300

4

更新:私が最初に思ったように、それはequalsをオーバーライドしないサブクラスのequalsの対称契約を破るため実際に、あなたの方法は、技術的には有効ではありません。

Point p = new Point(1, 2); 
Point pAnon = new Point(1, 1) { 
    @Override public int getY() { 
     return 2; 
    } 
}; 

System.out.println(p.equals(pAnon)); // prints false 
System.out.println(pAnon.equals(p)); // prints true 

理由はそのp.getClass().isAssignableFrom(pAnon.getClass())ですtrueであり、一方逆のpAnon.getClass().isAssignableFrom(p.getClass())falseである。

これで納得できない場合は、コードを実際に実行して記事のバージョンと比較してみてください。記事の例のようにtrue, false, trueの代わりにtrue, false, falseと表示されます。

+0

今、私は混乱しています。なぜ対称ではないのですか?私はここで何かを逃している。興味深いことに、pAnon.getClass()の結果は "ColoredPoint $ 1"です。 – smartnut007

+0

匿名クラスに対して異なるクラス署名を持つことは意味があります。 – smartnut007

1

すべての回答は質問に答えませんが、equals()契約を指摘します。等価は等価関係(推移的、対称的、再帰的)でなければならず、等しいオブジェクトは同じハッシュコードを持たなければなりません。サブクラス自体はequals()またはhashCode()をオーバーライドしません。だから、2つの選択肢があります - 確かPointColoredPointであるしなければならない今、あなたはどちらか(彼らは別の色を持っている場合でも、同じ座標を持っている場合はそうColoredPointインスタンスが等しい)Pointからequals()を継承するか、equals()を(オーバーライドし、等しくない)。

ポイントワイズ比較を実行する必要がある場合は、equals()を使用しないでください。代わりにpointwiseEquals()メソッドを記述してください。

あなたが何をしようとしても、あなたはまだequals()でクラスチェックを実行する必要があります。

​​

は明らかに最高のパフォーマーですが、あなた自身がequals()をオーバーライドする(実際には、あなたがそれを保証できる唯一の方法は作ることですしていない平等テストサブクラスにできることを期待していた場合、それが壊れるんクラスまたは等価メソッドfinalであり、サブクラスのオーバーライドをまったく許可しません)。 instanceOfisAssignableFromの間で選択した場合、実際には同じランタイムテストが実行されます(唯一の違いはinstanceOfです)。はコンパイル時の健全性チェックを実行しますが、この場合、入力がちょうどObjectのときは何も分かりません)。どちらの場合でも、実行時のチェックは同じです - オブジェクトのリストされたインタフェースの対象クラスをチェックします(ここではインターフェイスはチェックしていないので適用されません)。リストされているクラスに移動するか、ルートに移動します。我々はPoint.equals(ColoredPoint cp);

Point.equals()渡し

if (other instanceof Point)... 

のための最初のチェックを呼び出すとき

+0

ありがとうございます。しかし、@ワックスウィングの答えを見てください。 instanceofとisAssignableFromは同じ動作をしません。 – smartnut007

+0

@ user949300によって提起された設計上の配慮。 'other instanceof Point'は"このクラスは 'Point'オブジェクトを知っています - これは' Point'だからです。 'this.getClass()'と 'that.getClass()'を使いこなすと、あなたのコードはサブクラスについて仮定しています。 –

+0

@ChrisNash:基本クラスは、サブクラスに要件を課するあらゆる権利を持っているだけでなく、そのような前提を遵守することを前提としています。すべてのサブクラスインスタンスをそれと等しくないものとみなす基本クラスは、サブクラスがインスタンス間の任意の同値関係を定義できるようにします。特定のサブクラスインスタンスに等しいと報告することがある基底クラスは、すべてのそのようなサブクラスインスタンスが互いに等しいと自身を報告する必要があります。アプローチ#1は一般的に#2よりも安全で制約が少ない。 3番目のアプローチ... – supercat

1

ここで明確に質問

に私の第二の回答を検討しています。提示された3つのオプションのうち3つは、他のオブジェクト(この場合はColoredPoint)がもう少し多くのテストを満たしていることを確認します。オプションは次のとおりです。ポイントは決してありませんのinstanceof ColoredPointを、ある場合

  1. のみtrueになります
  2. ColoredPointはは
  3. は真ではありませんことはありませんポイントから割り当て可能である場合にのみtrueになります。実際の行動OPが望んでいるので、パフォーマンス(およびデザイン)の観点から

は、何も値が(彼は表現できなかった)、other instanceof Pointをチェックしてなかったことは、彼の特定のユースケースのためにある、これらの間の平等オブジェクトは、同じクラスでなければならないことを意味します。 @jthalborn

によって提案されたとして、後にコーダは、コード内でのinstanceofまたはisAssignableFrom見たとき

したがって、性能とデザインの両方のために、ちょうど

this.getClass() == that.getClass() 

を使用し、彼はサブクラスが許可されていると思います完全に誤解を招くベースクラスに等しくなるようにします。

0

OKここでは、Effective Java(私は第2版2008があります)の例があります。 例はITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALSの37ページから始まります(チェックしたい場合はこれを書いています)。

class ColoredPoint extends Point{}となり、なぜinstanceofがBADであるのかをデモストレーションするには2つの評価があります。最初の試みは、全ての第2 IFの最初に到達することはありません

// Broken - violates transitivity! 
@Override public boolean equals(Object o) { 
if (!(o instanceof Point)) 
    return false; 
// If o is a normal Point, do a color-blind comparison 
if (!(o instanceof ColorPoint)) 
    return o.equals(this); 
// o is a ColorPoint; do a full comparison 
return super.equals(o) && ((ColorPoint)o).color == color; 

}

// Broken - violates symmetry! 
@Override public boolean equals(Object o) { 
      if (!(o instanceof ColorPoint)) 
      return false; 
      return super.equals(o) && ((ColorPoint) o).color == color; 

}

と第二でした。 'o'がColorPointのスーパークラスであるPointでない場合、どうしたら非PointがColorPointとなるのでしょうか?

最初から2番目の試みは間違っています。真の比較のための唯一のチャンスはsuper.equals(o) && ((ColorPoint)o).color == color;で十分ではありません!ここ ソリューションは次のようになります。

if (super.equals(o)) return true; 

    if (!(o instanceof ColorPoint)) 
     if ((o instanceof Point)) return this.equals(o); 
     else return false; 

    return (color ==((ColorPoint)o).color && this.equals(o)); 

obj.getClass()は非常に特定のequals()のために使用されている、しかし、あなたの実装では、あなたの範囲を依存しています。 2つのオブジェクトが等しいかどうかをどのように定義しますか?それを実装するとそれに応じて動作します。