Java Crazyness - 当equals通过时包含失败

时间:2012-11-28 10:45:28

标签: java set equals contains

这是我在java(1.6)中看到的最疯狂的事情:

Set<ActionPlan> actionPlans = assessment.getActionPlans();
//getActionPlans() returns a java.util.HashSet<ActionPlan>
ActionPlan actionPlan = actionPlans.iterator().next();
assertTrue(actionPlan1.equals(actionPlan));
assertEquals(actionPlan1.hashCode(), actionPlan.hashCode());
assertTrue(actionPlans.contains(actionPlan1));

前两个断言通过,但最后一个断言。

我没有向您提供有关ActionPlan和评估课程的详细信息,因为它无关紧要。 contains方法失败,而equals和hash则没有。

我不是说java已经破坏或者其他任何东西,我的代码中可能会发生一些有趣的事情。

请注意,我是一名经验丰富的java程序员,我知道dos并不知道实现equals和hashCode。因此,如果我的代码中缺少某些东西,那就不是明显的了。

有没有人见过令人费解的东西?

修改

我在我的代码中做了一些研究,现在我认为问题在于休眠。我在创建之后,在代码的不同部分记录了ActionPlan对象的hashCode,直到调用失败的断言。 不会改变

我还检查了assessment.getActionPlans()返回的类,它是:

org.hibernate.collection.internal.PersistentSet

我很想相信Set的这个实现不会正确使用equals或hashcode。

有没有人对此有所了解?

3 个答案:

答案 0 :(得分:12)

可能有解释

  • 您有一个不使用equals或hashCode的有序集。
  • 你有“覆盖”等于(MyClass)而不是等于(对象)
  • hashCode使用的字段已更改。这使Set处于无法使用的状态。

测试最后一种可能性的最简单方法是尝试

assertTrue(new HashSet(actionPlans).contains(actionPlan1));

我怀疑这会在你的情况下通过。 ;)


Date有一个缺陷,它是可变的,hashCode使用那个可变字段,所以你可以通过改变它来破坏它所在的任何哈希集合。更改compareTo中使用的字段时会发生类似的问题。

Set<Date> dates = new HashSet<Date>();
SortedSet<Date> dates2 = new TreeSet<Date>();
Date d1 = new Date(1), d2 = new Date(2), d3 = new Date(3);
dates.add(d1);
dates.add(d2);
dates.add(d3);
dates2.add(d1);
dates2.add(d2);
dates2.add(d3);
d1.setTime(6);
d2.setTime(5);
d3.setTime(4);
System.out.print("The dates contains [");
for (Date date : dates) {
    System.out.print("date " + date.getTime() + " ");
}
System.out.println("]");
System.out.print("The sorted dates2 contains [");
for (Date date : dates2) {
    System.out.print("date " + date.getTime() + " ");
}
System.out.println("]");
for (int i = 1; i <= 6; i++)
    System.out.println("date " + i + " found is " + dates.contains(new Date(i))
            + " and " + dates2.contains(new Date(i)));

打印

The dates contains [date 6 date 5 date 4 ]
The sorted dates2 contains [date 6 date 5 date 4 ]
date 1 found is false and false
date 2 found is false and false
date 3 found is false and false
date 4 found is false and false
date 5 found is false and true
date 6 found is false and false

注意:已排序的集合现在的顺序错误。

答案 1 :(得分:4)

如果重载等于但覆盖 equals(Object),则会发生这种情况。

例如,您可能有:

public boolean equals(ActionPlan plan) {
    ...
}

将由以下人员调用:

assertTrue(actionPlan1.equals(actionPlan));

...但 不会被contains调用。你需要:

@Override public boolean equals(Object object) {
    ...
}

当然,这可能不是正在发生的事情。如果没有看到你的代码,我们就无法确定。

  

我没有向您提供有关ActionPlan和评估类的详细信息,因为它无关紧要。

这个答案与这个假设相矛盾......彼得的回答也是如此,其中包含替代失败模式。这就是为什么给出一个简短但完整的示例总是很重要的。

答案 2 :(得分:0)

在我做了equals和hashCode并使我的keyField成为最终之后,它仍然不起作用。 我花了一个小时才发现我在“compareTo”中需要这一行:

if (other != null && other.equals(this))
            return 0;