This is the most crazy thing I have seen in 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));
The first two asserts pass but the last one fails.
I'm not giving you details on the ActionPlan and Assessment classes because it shouldn't matter. The contains method fails where the equals and hash don't.
I'm not saying that java is broken or anything, there is probably something funny going on in my code.
Note that I'm an experienced java programmer and I'm aware of the dos and don't for implementing equals and hashCode. So if something is missing in my code it's not something obvious.
Has anyone ever seen something that puzzling?
EDIT
I did some research in my code and I now think the problem is in hibernate. I have logged the hashCode of the ActionPlan object, after creation, and at different parts of the code until the failing assert is getting called. It does not change.
Also I have checked the class returned by assessment.getActionPlans() and it is:
org.hibernate.collection.internal.PersistentSet
I'm tempted to believe that this implementation of Set does not use equals or hashcode properly.
Does anyone have insight on that?
There is possible explainations
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With