11

所以我一直在努力解决一个问题,我想我不妨在这里寻求帮助。

我将 Ticket 对象添加到 TreeSet,Ticket 实现了 Comparable 并覆盖了 equals()、hashCode() 和 CompareTo() 方法。我需要使用 contains() 检查对象是否已经在 TreeSet 中。现在,在向集合中添加 2 个元素之后,一切都很好,但是在添加了第三个元素之后,它就搞砸了。

在向 TreeSet 添加第三个元素后运行这段小代码,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 方法不一致,则可能会发生这种情况。即 if a.compareTo(b) > 0, thenb.compareTo(a)必须 < 0。如果a.compareTo(b) > 0and b.compareTo(c) > 0, thena.compareTo(c)必须 > 0。如果不是这样,TreeSet 可能会很混乱。

于 2010-08-08T02:00:04.227 回答
3

首先,如果您使用 a TreeSet,则方法的实际行为hashCode不会影响结果。 TreeSet不依赖散列。

真的,我们需要看更多的代码;equals例如和方法的实际实现compareTo,以及实例化TreeSet.

但是,如果我猜的话,那将是您通过使用签名声明方法重载了该方法。这将导致您所看到的行为。要获得所需的行为,您必须重写该方法;例如equalsboolean equals(Ticket other)

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

(最好在@Override注解中明确该方法覆盖超类中的方法,或实现接口中的方法。如果您的方法实际上不是覆盖,那么您将获得编译错误......这将是一件好事。)

编辑

根据您添加到问题中的代码,问题不在于重载与覆盖。(正如我所说,我只是在猜测......)

compareTo和很可能equals是不正确的。仍然不完全清楚错误的确切位置,因为这两种方法的语义都取决于类的compareToequals方法Boeking

第一个 if 语句Ticket.compareTo看起来非常可疑。看起来return 1;可能会导致t1.compareTo(t2)t2.compareTo(t1)返回1一些门票t1t2......这肯定是错误的。

于 2010-08-08T01:53:25.153 回答