Reputation: 181785
In Java, I have a subclass Vertex
of the Java3D class Point3f
. Now Point3f
computes equals()
based on the values of its coordinates, but for my Vertex
class I want to be stricter: two vertices are only equal if they are the same object. So far, so good:
class Vertex extends Point3f {
// ...
public boolean equals(Object other) {
return this == other;
}
}
I know this violates the contract of equals()
, but since I'll only compare vertices to other vertices this is not a problem.
Now, to be able to put vertices into a HashMap
, the hashCode()
method must return results consistent with equals()
. It currently does that, but probably bases its return value on the fields of the Point3f
, and therefore will give hash collisions for different Vertex
objects with the same coordinates.
Therefore I would like to base the hashCode()
on the object's address, instead of computing it from the Vertex
's fields. I know that the Object
class does this, but I cannot call its hashCode()
method because Point3f
overrides it.
So, actually my question is twofold:
equals()
?Edit: I just thought of something... I could generate a random int
value on object creation, and use that for the hash code. Is that a good idea? Why (not)?
Upvotes: 5
Views: 1914
Reputation: 405775
System.identityHashCode()
returns the same hash code for the given object as would be returned by the default method hashCode()
, whether or not the given object's class overrides hashCode()
.
Upvotes: 1
Reputation: 24262
Since you are not using equals as a logical comparison, but a physical one (i.e. it is the same object), the only way you will guarantee that the hashcode will return a unique value, is to implement a variation of your own suggestion. Instead of generating a random number, use UUID to generate an actual unique value for each object.
The System.identityHashCode() will work, most of the time, but is not guaranteed as the Object.hashCode() method is not guaranteed to return a unique value for every object. I have seen the marginal case happen, and it will probably be dependent on the VM implementation, which is not something you will want your code be dependent on.
Excerpt from the javadocs for Object.hashCode(): As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
The problem this addresses, is the case of having two separate point objects from overwriting each other when inserted into the hashmap because they both have the same hash. Since there is no logical equals, with the accompanying override of hashCode(), the identityHashCode method can actually cause this scenario to occur. Where the logical case would only replace hash entries for the same logical point, using the system based hash can cause it to occur with any two objects, equality (and even class) is no longer a factor.
Upvotes: 0
Reputation: 57324
Why do you want to override hashCode() in the first place? You'd want to do it if you want to work with some other definition of equality. For example
public class A { int id;
public boolean equals(A other) { return other.id==id} public int hashCode() {return id;}
} where you want to be clear that if the id's are the same then the objects are the same, and you override hashcode so that you can't do this:
HashSet hash= new HashSet(); hash.add(new A(1)); hash.add(new A(1)); and get 2 identical(from the point of view of your definition of equality) A's. The correct behavior would then be that you'd only have 1 object in the hash, the second write would overwrite.
Upvotes: 0
Reputation: 44613
Just FYI, your equals method does NOT violate the equals contract (for the base Object's contract that is)... that is basically the equals method for the base Object method, so if you want identity equals instead of the Vertex equals, that is fine.
As for the hash code, you really don't need to change it, though the accepted answer is a good option and will be a lot more efficient if your hash table contains a lot of vertex keys that have the same values.
The reason you don't need to change it is because it is completely fine that the hash code will return the same value for objects that equals returns false... it is even a valid hash code to just return 0 all the time for EVERY instance. Whether this is efficient for hash tables is completely different issue... you will get a lot more collisions if a lot of your objects have the same hash code (which may be the case if you left hash code alone and had a lot of vertices with the same values).
Please don't accept this as the answer though of course (what you chose is much more practical), I just wanted to give you a little more background info about hash codes and equals ;-)
Upvotes: 0
Reputation: 32181
You use a delegate even though this answer is probably better.
class Vertex extends Point3f{
private final Object equalsDelegate = new Object();
public boolean equals(Object vertex){
if(vertex instanceof Vertex){
return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate);
}
else{
return super.equals(vertex);
}
}
public int hashCode(){
return this.equalsDelegate.hashCode();
}
}
Upvotes: 0
Reputation: 70211
Either use System.identityHashCode() or use an IdentityHashMap.
Upvotes: 10
Reputation: 4884
The function hashCode() is inherited from Object and works exactly as you intend (on object level, not coordinate-level). There should be no need to change it.
As for your equals-method, there is no reason to even use it, since you can just do obj1 == obj2 in your code instead of using equals, since it's meant for sorting and similar, where comparing coordinates makes a lot more sense.
Upvotes: -2