Treeset.contains()问题

时间:2010-08-08 01:45:30

标签: java contains treeset

所以我现在一直在努力解决问题,我想在这里也可以寻求帮助。

我将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

3 个答案:

答案 0 :(得分:18)

compareTo合同

问题出在compareTo。以下摘自the documentation

  

执行者必须确保所有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语句,但事实上,逻辑流程非常易读。另请注意,排序标准的优先级是如何明确的,如果您考虑到不同的优先级,则可以轻松重新排序。

相关问题

答案 1 :(得分:4)

如果你的compareTo方法不一致,可能会发生这种情况。即如果a.compareTo(b) > 0,那么b.compareTo(a)必须是&lt; 0.如果a.compareTo(b) > 0b.compareTo(c) > 0,那么a.compareTo(c)必须是&gt;如果那些不是真的,TreeSet会让所有人感到困惑。

答案 2 :(得分:3)

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

我们真的需要看到更多代码;例如equalscompareTo方法的实际实现,以及实例化TreeSet的代码。

但是,如果我猜测,那就是重载 equals方法,方法是使用签名boolean equals(Ticket other)进行声明。这会导致你所看到的行为。要获得所需的行为,您必须覆盖方法; e.g。

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

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

修改

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

compareToequals很可能不正确。由于两种方法的语义都取决于compareTo类的equalsBoeking方法,因此仍然不完全清楚错误的确切位置。

Ticket.compareTo的第一个if语句看起来非常可疑。看起来return 1;可能导致t1.compareTo(t2)t2.compareTo(t1)同时返回1某些机票t1t2 ...... 绝对是错误的。