web-dev-qa-db-ja.com

Java:カスタムオブジェクトでいっぱいのArrayListでcontainsを使用するには、equalsをオーバーライドするか、Comparable / Comparatorを実装する必要がありますか?

これらでいっぱいのArrayListがあります:

class TransitionState {

    Position positionA;
    Position positionB;

    int counter;

    public boolean equals (Object o){

        if (o instanceof TransitionState){

          TransitionState transitionState= (TransitionState)o;

          if ((this.positionA.equals(transitionState.positionA))
                  &&(this.positionB.equals(transitionState.positionB)))
          {
              return true;
          }
        }
     return false;

    }

    @Override
    public String toString() {

        String output = "Position A " + positionA.i+ " "+ positionA.j + " "+ positionA.orientation + " "+
                "Position B " + positionB.i + " "+ positionB.j + " "+ positionB.orientation;

        return output;
    }

}

class Position {

    int i;
    int j;
    char orientation;

    Position() {

    }


    void setIJ(int i, int j){
        this.i=i;
        this.j=j;
    }

    void setOrientation(char c){

        orientation = c;
    }

   public boolean equals(Object o){

        if(o instanceof Position){

          Position p = (Position)o;
          if((p.i==this.i)&&(p.j==this.j)&&(p.orientation==this.orientation))
          {
              return true;
          }
              else return false;

        }

            return false;
   }

} //end class Position

私はこれでそれをクエリします:

 if(!transitionStatesArray.contains(newTransitionState)){  //if the transition state is new add and enqueue new robot positions

                 transitionStatesArray.add(newTransitionState); //marks as visited

私のtransitionStatesArray内に重複する要素が見つかりました。なぜですか?

これらのi、jと方向の値を使用して、マトリックス内の一意の値を入力していますが、ここでは重複しています:

 S  .  N 
 *  *  * 
 .  D  D 


 E  .  O 
 *  *  * 
 .  D  D 


 N  .  S 
 *  *  * 
 .  D  D 


 S  .  N 
 *  *  * 
 .  D  D 
19
andandandand

List.contains(...)メソッドは、equals(Object)を使用して、引数オブジェクトがリストに「含まれる」かどうかを決定するように定義されています。したがって、equals ...をオーバーライドする必要があります。デフォルトの実装が必要なものでないと仮定します。

ただし、List.contains(...)はリスト内のすべての要素に対して引数をテストする可能性があることに注意する必要があります。長いリストの場合、それは高価です。アプリケーションの詳細に応じて、別のコレクションタイプ(例:HashSetTreeSetまたはLinkedHashSet)の代わりにListを使用します。これらのいずれかを使用する場合、クラスはhashCodeをオーバーライドするかComparableを実装するか、選択した内容に応じて別のComparator ...を作成する必要があります。 。


(OPが関心を持っているため、代替案についてもう少しアドバイス)

containsListのようなArrayListタイプでのLinkedListのパフォーマンスはO(N)です。 contains呼び出しの最悪の場合のコストは、リストの長さに正比例します。

TreeSetの場合、containsの最悪の場合のパフォーマンスはlog2(N)に比例します。

HashSetまたはLinkedHashSetの場合、containsの平均パフォーマンスは定数であり、コレクションのサイズとは無関係ですが、最悪の場合のパフォーマンスはO(N)。 (最悪の場合のパフォーマンスは、1)すべてを少数の値にハッシュする貧弱なhashcode()関数を実装するか、2)「負荷係数」パラメーターを微調整して、ハッシュテーブルが自動的にサイズ変更されないようにします成長するにつれて)

Setクラスを使用することの欠点は次のとおりです。

  • それらはsets;つまり、2つ以上の「等しい」オブジェクトをコレクションに入れることはできません。
  • インデックスを作成することはできません。例えばget(pos)メソッドはありません。
  • 一部のSetクラスは、挿入順序を保持しません。

使用するコレクションクラスを決定するときは、これらの問題を考慮する必要があります。

28
Stephen C

おそらくhashCode()を実装する必要があります。一般に、とにかく等号をオーバーライドするときは、常にこれを行う必要があります。

collections docs から:

この仕様は、null以外の引数oを指定してCollection.containsを呼び出すと、任意の要素eに対してo.equals(e)が呼び出されることを意味するものと解釈してはなりません。実装は、最適化を自由に実装できます。これにより、たとえば最初に2つの要素のハッシュコードを比較することにより、equalsの呼び出しが回避されます。 (Object.hashCode()仕様は、等しくないハッシュコードを持つ2つのオブジェクトが等しくなることを保証します。)より一般的には、さまざまなCollections Frameworkインターフェースの実装は、実装者が適切であると見なす場合は常に、基礎となるObjectメソッドの指定された動作を自由に利用できます。 。

1
verdesmarald