I don't usually code in Java, but recently I started not having a choice. I might have some major misunderstanding of how to properly use HashSet. So it might be possible something I did is just plain wrong. However I'm grateful for any help, you might offer. So the actual problem:
In a small program I was writing, I was generating very similar objects, which, when created, would have a very specific id (a string
or in my last iteration a long
). Because each object would spawn new objects, I wanted to filter out all those I already created. So I started throwing the id of every new object into my Hash(Set) and testing with HashSet.contains()
, if an object was created before. Here is the complete code:
// hashtest.java
import java.util.HashSet;
class L {
public long l;
public L(long l) {
this.l = l;
}
public int hashCode() {
return (int)this.l;
}
public boolean equals(L other) {
return (int)this.l == (int)other.l;
}
}
class hashtest {
public static void main(String args[]) {
HashSet<L> hash = new HashSet<L>();
L a = new L(2);
L b = new L(2);
hash.add(a);
System.out.println(hash.contains(a));
System.out.println(hash.contains(b));
System.out.println(a.equals(b));
System.out.println(a.hashCode() == b.hashCode());
}
}
produces following output:
true
false
true
true
so apparently, contains
does not use the equals
function provided by L
, or I have some major misunderstanding of the concept ...
I tested it with openjdk (current version included in ubuntu) and the official current java from Oracle on Win7
for completeness official java-api documentation for HashSet.contains()
:
public boolean contains(Object o)
Returns
true
if this set contains the specified element. More formally, returnstrue
if and only if this set contains an elemente
such that(o==null ? e==null : o.equals(e))
.
http://download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)
Any ideas or suggestions?
When you are adding objects to a set it internally calls
equals
andhashCode
methods. You have to override these two methods. For example I have taken one bean class withname
,id
,designation
, then created and added anemployee
object.set.add()
calls internally theequals
andhashCode
methods. So you have to override these two methods in your bean class.Here we are overriding
equals()
andhashCode()
. When you add an object to theHashSet
method it internally iterates all objects and calls theequals
method. Hence we overridhashCode
, it compares every objectshashCode
with its currenthashCode
and returns true if both are equal, else it returns false.Your
equals
method needs to take anObject
.Because you declared it as taking an
L
, it becomes an additional overload instead of overriding the method.Therefore, when the
hashSet
class callsequals
, it resolves to the baseObject.equals
method. When you callequals
, you call your overload becausea
andb
are both declared asL
instead ofObject
.To prevent this issue in the future, you should add
@Override
whenever you override a method.This way, the compiler will warn you if it isn't actually an override.
You're not actually overriding
Object.equals
; instead, you're defining a new method with the same name but different parameters. Notice thatObject.equals
takes anObject
argument, while your equals method takes anL
argument. If you rewrite your equals method to take anObject
and perform the necessary type-checking/casting toL
at runtime, then your code is work as you expect.Also, this is why you really should use
@Override
annotations whenever your JRE supports them. That way, the compiler will complain if you accidentally implement a new method when you intend to override an existing one.By way of an example, this equals method should work correctly. (And, on an unrelated note, it won't fail if the object being compared to is null.)