Java HashSet Problem

T

twizansk

Hello,

I am having a problem using a hashset for user defined objects.
Identical objects are not being recognized as identical.

Here is the relevant code:

import java.util.*;

class MyString {

MyString(String s, int i) {
this.s=s;
this.i=i;
}

private String s;
private int i;

public boolean equals(MyString mystring) {

return (s.equals(mystring.s) && i==mystring.i);

}

public int hashCode() {

int result=1;
result=37*result + i;
result=37*result + s.hashCode();

return result;

}

}

public class Temp {

public static void main(String[] args) {

MyString key1 = new MyString("ten",10);
MyString key2 = new MyString("ten",10);

HashSet<MyString> hset = new HashSet<MyString>();
hset.add(key1);
System.out.println("hashset contains key1: " + hset.contains(key1));
System.out.println("hashset contains key2: " + hset.contains(key2));
System.out.println("key1.equals(key2): " + key1.equals(key2));
System.out.println("key2.equals(key1): " + key2.equals(key1));
System.out.println("key1 hashcode: " + key1.hashCode());
System.out.println("key2 hashcode: " + key2.hashCode());

}

}


The output is:

hashset contains key1: true
hashset contains key2: false
key1.equals(key2): true
key2.equals(key1): true
key1 hashcode: 116456
key2 hashcode: 116456


Apparently, the hashset doesn't realize that key1 and key2 are equal
even though the hash codes are equal and the equals method is
implemented correctly.

Does anyone know what I'm doing wrong?

Thanks
 
A

Andrew Thompson

On Mar 14, 10:53 am, (e-mail address removed) wrote:
...
I am having a problem using a hashset for user defined objects.
Identical objects are not being recognized as identical.

Huh? Your output suggests otherwise.*
Here is the relevant code:

As an aside. Good example code. Compiled and
ran (producing identical results here) without
changes.
The output is:

hashset contains key1: true
hashset contains key2: false
key1.equals(key2): true
key2.equals(key1): true

* Here it is saying that it recognises that key1
and key2 *are* the same according to the specified
test of equality.
Does anyone know what I'm doing wrong?

I do not understand your question. Did I
miss something?
 
T

twizansk

On Mar 14, 10:53 am, (e-mail address removed) wrote:
...


Huh? Your output suggests otherwise.*


As an aside. Good example code. Compiled and
ran (producing identical results here) without
changes.



* Here it is saying that it recognises that key1
and key2 *are* the same according to the specified
test of equality.


I do not understand your question. Did I
miss something?

Sorry. I'll clarify the question. key1 and key2 are recognized as
equal using the equals method and they do generate the same hash
code. Therefore, when I insert key1 into hset and ask if the set
contains key2 (using the contains() method) the answer should be yes.
However, as you can see, the hash set does not realize that it
contains key2.

I hope that clarifies the problem I'm having.

Thanks
 
A

Arved Sandstrom

(e-mail address removed) wrote:

[ SNIP ]
Sorry. I'll clarify the question. key1 and key2 are recognized as
equal using the equals method and they do generate the same hash
code. Therefore, when I insert key1 into hset and ask if the set
contains key2 (using the contains() method) the answer should be yes.
However, as you can see, the hash set does not realize that it
contains key2.

I hope that clarifies the problem I'm having.

Thanks

Except, where did you add key2? :)

AHS
 
L

Lew

import java.util.*;

class MyString {

MyString(String s, int i) {
this.s=s;
this.i=i;
}

private String s;
private int i;

public boolean equals(MyString mystring) {

return (s.equals(mystring.s) && i==mystring.i);

}

public int hashCode() {

int result=1;
result=37*result + i;
result=37*result + s.hashCode();

return result;

}

}

[ SNIP ]
However, as you can see, the hash set does not realize that it
contains key2.

You have to override
<http://java.sun.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object)>
not overload it.

Arved said:
Except, where did you add key2? :)

That was the whole point - he didn't. The collection was supposed to compare
physically distinct objects as logically equals(). It should have returned
that key2 was contained. Except for the failure to override equals().
 
E

Eric Sosman

Hello,

I am having a problem using a hashset for user defined objects.
Identical objects are not being recognized as identical.
[...]
public boolean equals(MyString mystring) {
[...]

HashSet is going to call the equals(Object) method, not
the equals(MyString) method. Since you have not overridden
equals(Object), you wind up using the version from Object
itself, which declares any two objects different unless they
are in fact the same instance.
 
R

Roedy Green

int result=1;
result=37*result + i;
result=37*result + s.hashCode();

return result;

why did you write that in such a meandering way? You could have got
the same essential effect with

return i * 37 + s.hashCode();
or
return i ^ s.hashCode();
or even
return i + s.hashCode();
 
R

Roedy Green

Sorry. I'll clarify the question. key1 and key2 are recognized as
equal using the equals method and they do generate the same hash
code. Therefore, when I insert key1 into hset and ask if the set
contains key2 (using the contains() method) the answer should be yes.
However, as you can see, the hash set does not realize that it
contains key2.

"List the output and the output you expected. In a certain sense, your
program nearly always works perfectly. It is just that you
misunderstood what that piece of code was supposed to produce. It
usually produces a result completely in conformance with what you
asked for; it is just not the result you wanted."
~ http://mindprod.com/jgloss/sscce.html
 
P

Peter Duniho

why did you write that in such a meandering way? You could have got
the same essential effect with

I'm not the OP, but the code was probably written that way because it more
clearly states the algorithm being used, can be more easily modified if
the members used to calculate the hash code change (just copy-and-paste
lines for new members or delete removed ones), and because any decent
compiler can easily turn it into practically the equivalent of the
most-correct alternative you posted (there's an extra add, but that's no
big deal).

In other words, the main thing the examples you posted accomplish is
obfuscating the calculation. I realize that's a goal for some
programmers, but many of us prefer code that's more maintainable.

Pete
 
R

Roedy Green

HashSet.contains is defined as
Returns true if this set contains the specified element. More
formally, returns true if and only if this set contains an element e
such that (o==null ? e==null : o.equals(e)).

so you DON'T need an == match to count as contains.

I think you have found a bug.

Have a peek at the code in src.zip to see if you can see if they are
doing an == instead of an equals for contains.
 
A

Andrew Thompson

...The collection was supposed to compare
physically distinct objects as logically equals().  It should have returned
that key2 was contained.  Except for the failure to override equals().

It took me a while to understand your Zen-like answer.

Now having solved it, and now with some enthusiasm,
I intend to reduce your beautiful answer to something
far more 'common'.

public boolean equals(Object mystring) {

if ( mystring instanceof MyString ) {
MyString astring = (MyString)mystring;
return (s.equals(astring.s) && i==astring.i);
} else {
return false;
}
}
 
A

Andrew Thompson

Dang!  How did I miss that first sentence?!

..not to mention Eric's even more direct answer!

I'm going to retreat to a quiet place for
a (short) while and nurse my wounded pride. :-(
 
L

Lew

Roedy said:
HashSet.contains is defined as
Returns true if this set contains the specified element. More
formally, returns true if and only if this set contains an element e
such that (o==null ? e==null : o.equals(e)).

so you DON'T need an == match to count as contains.

I think you have found a bug.

Yes, in the OP's code.
Have a peek at the code in src.zip to see if you can see if they are
doing an == instead of an equals for contains.

They're using equals( Object ), not equals( MyString ), for contains().
 
L

Lew

Andrew said:
I'm going to retreat to a quiet place for
a (short) while and nurse my wounded pride. :-(

Actually, your pride should be strengthened. After all, you did figure it out
on the earliest hints, before resorting to the easy answers.
 
P

Peter Duniho

Don't be silly. My examples are much clearer.

I didn't expect you to agree with me; your reputation precedes you.
Suffice to say though, your own opinion regarding readability and clarity
is not the last word on the matter.

Pete
 
G

Gordon Beaton


Indeed. Perhaps if you actually *read* the thread before posting your
reply, you would have seen that the correct answer had already been
posted twice by others.

/gordon

--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top