11

だから私はしばらくの間問題に苦しんでいて、ここで助けを求めたほうがいいと思いました。

TreeSetにTicketオブジェクトを追加しています。TicketはComparableを実装し、equals()、hashCode()、およびCompareTo()メソッドをオーバーライドしています。contains()を使用して、オブジェクトがすでにTreeSetにあるかどうかを確認する必要があります。セットに2つの要素を追加すると、すべて正常にチェックアウトされますが、3つ目の要素を追加すると、混乱します。

TreeSetに3番目の要素を追加した後、この小さなコードを実行すると、Ticket temp2がチェックしているオブジェクト(verkoopLijst)になります。

    Ticket temp2 = new Ticket(boeking, TicketType.STANDAARD, 1,1);
    System.out.println(verkoop.getVerkoopLijst().first().hashCode());
    System.out.println(temp2.hashCode());

    System.out.println(verkoop.getVerkoopLijst().first().equals(temp2));
    System.out.println(verkoop.getVerkoopLijst().first().compareTo(temp2));
    System.out.println(verkoop.getVerkoopLijst().contains(temp2));

これを返します:

22106622
22106622
true
0
false

今私の質問は、これがどのように可能であるかということです。

編集:

public class Ticket implements Comparable{

    private int rijNr, stoelNr;
    private TicketType ticketType;
    private Boeking boeking;


    public Ticket(Boeking boeking, TicketType ticketType, int rijNr, int stoelNr){    
        //setters
    }

    @Override
    public int hashCode(){
        return boeking.getBoekingDatum().hashCode();     
    }

    @Override
    @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")    
    public boolean equals(Object o){
       Ticket t = (Ticket) o;

       if(this.boeking.equals(t.getBoeking())
               &&
          this.rijNr == t.getRijNr() &&  this.stoelNr == t.getStoelNr()
               &&
          this.ticketType.equals(t.getTicketType()))
       {
           return true;
       }

       else return false;

    }

    /*I adjusted compareTo this way because I need to make sure there are no duplicate Tickets in my treeset. Treeset seems to call CompareTo() to check for equality before adding an object to the set, instead of equals().


     */
    @Override
    public int compareTo(Object o) {
        int output = 0;
        if (boeking.compareTo(((Ticket) o).getBoeking())==0)
        {
            if(this.equals(o))
            {
                return output;
            }
            else return 1;
        }
        else output = boeking.compareTo(((Ticket) o).getBoeking());
        return output;
    }

    //Getters & Setters
4

3 に答える 3

18

契約compareTo

問題はあなたにありますcompareToドキュメントからの抜粋は次のとおりです。

実装者は、 および のすべてについて確認する必要がありsgn(x.compareTo(y)) == -sgn(y.compareTo(x))ます。xy

元のコードを参照用にここに再現します。

// original compareTo implementation with bug marked

@Override
public int compareTo(Object o) {
    int output = 0;
    if (boeking.compareTo(((Ticket) o).getBoeking())==0)
    {
        if(this.equals(o))
        {
            return output;
        }
        else return 1; // BUG!!!! See explanation below!
    }
    else output = boeking.compareTo(((Ticket) o).getBoeking());
    return output;
}

なぜreturn 1;バグなのですか?次のシナリオを検討してください。

  • 与えられたTicket t1, t2
  • 与えられたt1.boeking.compareTo(t2.boeking) == 0
  • 与えられt1.equals(t2)たリターンfalse
  • これで、次の両方が得られました。
    • t1.compareTo(t2)戻り値1
    • t2.compareTo(t1)戻り値1

その最後の結果は契約違反です。compareTo


問題の修正

何よりもまずComparable<T>、パラメーター化可能なジェネリック型であるという事実を利用する必要がありました。つまり、次の代わりに:

// original declaration; uses raw type!
public class Ticket implements Comparable

代わりに、次のように宣言する方がはるかに適切です。

// improved declaration! uses parameterized Comparable<T>
public class Ticket implements Comparable<Ticket>

compareTo(Ticket)これで( ではなく)を書くことができますcompareTo(Object)。これを書き直すには多くの方法がありますが、ここではかなり単純化して機能する方法を示します。

@Override public int compareTo(Ticket t) {
   int v;

   v = this.boeking.compareTo(t.boeking);
   if (v != 0) return v;

   v = compareInt(this.rijNr, t.rijNr);
   if (v != 0) return v;

   v = compareInt(this.stoelNr, t.stoelNr);
   if (v != 0) return v;

   v = compareInt(this.ticketType, t.ticketType);
   if (v != 0) return v;

   return 0;
}
private static int compareInt(int i1, int i2) {
   if (i1 < i2) {
     return -1;
   } else if (i1 > i2) {
     return +1;
   } else {
     return 0;
   }
}

これで、逆ではなく、のequals(Object)観点から定義することもできます。compareTo(Ticket)

@Override public boolean equals(Object o) {
   return (o instanceof Ticket) && (this.compareTo((Ticket) o) == 0);
}

の構造に注意してくださいcompareTo。複数のreturnステートメントがありますが、実際には、ロジックの流れは非常に読みやすいです。また、並べ替え基準の優先順位が明示的であり、異なる優先順位を念頭に置いている場合に簡単に並べ替えることができることにも注意してください。

関連する質問

于 2010-08-08T08:02:27.867 に答える
4

これは、compareTo メソッドが一貫していない場合に発生する可能性があります。a.compareTo(b) > 0つまり、ifはb.compareTo(a)< 0 でなければなりません。また、 ifa.compareTo(b) > 0およびb.compareTo(c) > 0a.compareTo(c)> 0 でなければなりません。これらが真でない場合、TreeSet はすべて混乱する可能性があります。

于 2010-08-08T02:00:04.227 に答える
3

まず、を使用している場合TreeSet、メソッドの実際の動作はhashCode結果に影響しません。 TreeSetハッシュに依存しません。

本当にもっと多くのコードを見る必要があります。equalsたとえば、とメソッドの実際の実装、およびcompareToをインスタンス化するコードTreeSet

ただし、推測すると、シグネチャを使用してメソッドを宣言することにより、メソッドをオーバーロードした可能性があります。それはあなたが見ている行動につながるでしょう。必要な動作を得るには、メソッドをオーバーライドする必要があります。例えばequalsboolean equals(Ticket other)

@Override
public boolean equals(Object other) { ...

@Overrideメソッドがスーパークラスのメソッドをオーバーライドするか、インターフェイスにメソッドを実装することを明確にするために、アノテーションを挿入することをお勧めします。メソッドが実際にオーバーライドでない場合は、コンパイルを取得しますエラー...これは良いことです。)

編集

質問に追加したコードに基づくと、問題はオーバーロードとオーバーライドではありません。(私が言ったように、私は推測しているだけでした...)

compareToequalsが正しくない可能性があります。両方のメソッドのセマンティクスはクラス のメソッドcompareToとメソッドに依存するため、バグがどこにあるのかはまだ完全には明らかではありません。equalsBoeking

Ticket.compareTo見た目の最初のifステートメントは非常に疑わしいようです。いくつかのチケットと...の両方return 1;を引き起こす可能性がありt1.compareTo(t2)、それは間違いなく間違っているようです。t2.compareTo(t1)1t1t2

于 2010-08-08T01:53:25.153 に答える