I have a class hierarchy as follows: ModelElement is extended by Entity. Entity is extended by StateEntity and CompositeEntity. All three classes override Object.hashCode() and Object.equals(). I have an object of type HashSet<Entity> which contains CompositeEntity objects. I wish to determine if a particular CompositeEntity object is present in the HashSet, so I call HashSet.contains() with the CompositeEntity as argument. This returns false. However, iterating through the HashSet and comparing the values returned by hashCode() and equals() shows that there is an object contained in the set for which the hashCode matches that which is being searched for, and using equals() to compare them returns true. Given this, how is it possible that contains() returns false if HashSet uses hashCode() and equals() to determine object equality? Only one thread is operating on the HashSet.
For a proper test you need to compare hashCode() first as HashSet() does and then equals()
If two objects which are equal() but different hashCode()s will never get a match
e.g.
publicclass A {
privatefinalint id;
privatefinal String name;
public A (int id, String name) {
this.id = id;
this.name = name;
}
publicint hashCode() {
return id;
}
publicboolean equals(Object obj) {
if (obj instanceof A) {
return name.equals(((A) obj).name);
returnfalse;
}
}
//
A a1 = new A(1, "one");
A a2 = new A(2, "one");
// a1.equals(a2) but a1.hashCode() != a1.hashCode();
I call HashSet.contains() with the
CompositeEntity as argument. This returns false.
However, iterating through the HashSet and comparing
the values returned by hashCode() and equals() shows
that there is an object contained in the set for
which the hashCode matches that which is being
searched for, and using equals() to compare them
returns true.
Is it possible that your equals method is not symmetric?
Let element be the element that you retrieve by iterating through the set. Let entity be the CompositeEntity instance that you passed as a parameter to contains().
You say that the equals method returns true for these two objects (and that they have the same hashCode). But does equals return true in both directions? ie
I would surmise that your CompositeEntity uses mutable attributes in its hash code calculation. You can't do that and expect a HashSet to store it properly. The hashCode must be immutable, and so must be the equals, to use HashSet; here's an example of how doing otherwise can cause a problem.
Step 1: define the CompositeEntity like this:
CompositeEntity has a public String variable called p. Its hash code is the hash code of p; its equality is if p's are equal.
CompositeEntity one = new CompositeEntity();
CompositeEntity two = new CompositeEntity();
one.p = "this";
HashSet test = new HashSet();
test.add(one);
The above code adds the entity one at the hash code value of "this"
Because it's looking now at the hash code of "that" and one isn't there, it's at the hash code of "this" which was its hash code when it was added.
----
To the 3rd poster, with the code sample: you forgot to override hashCode() in your example, that's why it isn't working right. NOT the same problem as the OP.
To the 3rd poster, with the code sample: you forgot
to override hashCode() in your example, that's why it
isn't working right. NOT the same problem as the OP.
Right. And here's a reasonable recipe for how:
http://developer.java.sun.com/developer/Books/effectivejava/Chapter3.pdf
I would surmise that your CompositeEntity uses
mutable attributes in its hash code calculation. You
can't do that and expect a HashSet to store it
properly. The hashCode must be immutable,
Or you have to be able to detect when any of the hashcode-relevant fields changes, and then rehash or remove and re-add the entry.