【问题标题】:Java Crazyness - Contains fails when equals passesJava Crazyness - 当等于通过时,包含失败
【发布时间】:2012-11-16 04:39:08
【问题描述】:

这是我在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 和 Assessment 课程的详细信息,因为这无关紧要。 contains 方法在 equals 和 hash 没有的地方失败。

我并不是说 java 坏了或其他什么,我的代码中可能发生了一些有趣的事情。

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

有没有人见过令人费解的事情?

编辑

我对我的代码进行了一些研究,现在我认为问题出在休眠状态。我已经记录了 ActionPlan 对象的 hashCode,在创建之后以及代码的不同部分,直到调用失败的断言。 不会改变

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

org.hibernate.collection.internal.PersistentSet

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

有人对此有见解吗?

【问题讨论】:

  • 另请注意,Assessment 和 ActionPLan 对象是使用 TRANSACTION 类型的持久性上下文检索的 JPA(使用休眠实现)实体。
  • 您能显示ActionPlan 的等号和哈希码吗?这应该很重要。
  • 我认为发布您的代码 - 特别是 equals/hashCode 是 key 到此
  • 您已经承认“[您的]代码中可能发生了一些有趣的事情”——但您认为该代码无关紧要?奇数。
  • 根据 Set 对象的类,我们可能还需要 compareTo 或 Comparator 实现,以及 equals 和 hashCode 声明。例如,在目前有限的信息中,我们可以处理一个 TreeSet 和一个 compareTo 与 equals 不一致的类。

标签: java set equals contains


【解决方案1】:

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

例如,您可能有:

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

这将被调用:

assertTrue(actionPlan1.equals(actionPlan));

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

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

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

我不会向您提供有关 ActionPlan 和 Assessment 课程的详细信息,因为这无关紧要。

这个答案与那个假设相矛盾......就像彼得的答案一样,其中包含 alternative 故障模式。这就是为什么给出一个简短但完整的例子总是很重要的原因。

【讨论】:

  • 正如我所提到的,我知道良好的做法,并且我确实覆盖(而不是重载)等于。不过还是谢谢你的建议。
  • @phoenix7360:仅仅因为您声称了解良好做法并不意味着您实际上确实了解它们。这就是您应该提供代码的原因(这是另一个好的做法)。
  • 我明白你的意思。我没有提供我的代码的原因是因为这些类非常大,equals 和 hashCode 方法也是如此。他们还持有一些我不允许发布的商业信息。无论如何,再次感谢您的快速回答!
  • @phoenix7360:这就是为什么值得尝试将其缩减为一个可以证明问题的最小示例,并且没有敏感信息。在尝试这样做的过程中,您很可能已经发现了问题。
【解决方案2】:

有可能的解释

  • 您有一个不使用 equals 或 hashCode 的排序集。
  • 您已经“覆盖”了 equals(MyClass) 而不是 equals(Object)
  • 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

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

【讨论】:

  • 确实,您的测试通过了 :) 那么是不是 hibernate 正在返回某种不使用 equals 或 hashCode 的自制集合?
  • 不,这意味着最后一种情况是正确的,即 hashCode 中使用的字段在添加到 Set 后被更改,因此它将以未定义的方式运行。
  • 是的,我想大概就是这样。很好看的彼得!
  • 我分析了我的代码(检查我的问题中的编辑)并且哈希码永远不会改变。我认为问题在于hibernate(org.hibernate.collection.internal.PersistentSet)返回的集合的实现
【解决方案3】:

在我执行了 equals 和 hashCode 并将我的 keyField 设置为 final 之后,它仍然无法正常工作。 我又花了一个小时才发现我在“compareTo”中需要这一行:

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

【讨论】:

    猜你喜欢
    • 2021-04-08
    • 2017-04-01
    • 1970-01-01
    • 2017-01-07
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    相关资源
    最近更新 更多