Java Crazyness - Contains fails when equals passes

核能气质少年 提交于 2019-12-05 16:42:16

There is possible explainations

  • You have a sorted set which doesn't use equals or hashCode.
  • You have "overriden" equals(MyClass) instead of equals(Object)
  • The fields used by the hashCode are changed. This leave the Set in a state which is not usable.

The simplest way to test the last possibility is to try

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

I suspect this will pass in your case. ;)


Date has a flaw that it is mutable and hashCode uses that mutable fields so you can corrupt any hash collection it is in by mutating it. A similar problem occurs when you alter a field which is used in 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)));

prints

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

Note: the sorted collection is now in the wrong order.

This will happen if you overload equals but don't override equals(Object).

For example, you may have:

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

That will be called by:

assertTrue(actionPlan1.equals(actionPlan));

... but will not be called by contains. You need:

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

Of course, it's possible that that's not what's happening. There's no way we can tell for sure without seeing your code.

I'm not giving you details on the ActionPlan and Assessment classes because it shouldn't matter.

This answer contradicts that assumption... as does Peter's answer, which contains alternative failure modes. This is why giving a short but complete example is always important.

After I did equals and hashCode and made my keyField final, it still doesn't work. It took me another hour to find out that I needed this line in the "compareTo":

if (other != null && other.equals(this))
            return 0;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!