How HashMap performs a get?

A

andrew.tanenbaum

Hi, I have a problem with a HashMap. I store a couple key-value of type
MyClass-Integer, but when I try to get the value, it returns null, even
if the keys seem equal. For example, suppose that MyClass has an
attribute called "name", which value is passed in the constructor, if I
do something like:

MyClass obj1 = new MyClass("MyName");
myHashMap.put(obj1, 1);

MyClass obj2 = new MyClass("MyName");
Integer value = myHashMap.get(obj2);

It should return something, and not null.
My question is, how a hashmap verifies if it contains the key "obj2"? I
suppose that it invokes the method "equals" of the class MyClass. So,
if I redefine the "equals" method in this way

public boolean equals(Object o){
if(o instanceof MyClass)
return name.equals(((MyClass)o).getName())
else
return super.equals(o);
}

it should works? Or not?

Thanks
 
M

Matt Humphrey

Hi, I have a problem with a HashMap. I store a couple key-value of type
MyClass-Integer, but when I try to get the value, it returns null, even
if the keys seem equal. For example, suppose that MyClass has an
attribute called "name", which value is passed in the constructor, if I
do something like:

MyClass obj1 = new MyClass("MyName");
myHashMap.put(obj1, 1);

MyClass obj2 = new MyClass("MyName");
Integer value = myHashMap.get(obj2);

It should return something, and not null.
My question is, how a hashmap verifies if it contains the key "obj2"? I
suppose that it invokes the method "equals" of the class MyClass. So,
if I redefine the "equals" method in this way

public boolean equals(Object o){
if(o instanceof MyClass)
return name.equals(((MyClass)o).getName())
else
return super.equals(o);
}

Yes, you must redefine equals, but you must also provide a compatible
definition of hashCode(). Hashing uses the hash code as a first, very fast
approximation. Two keys that are equal must produce the same hash code,
although other keys can sometimes produce the same code.

Check out the details here http://mindprod.com/jgloss/hashcode.html#EQUALS

Cheers,
Matt Humphrey (e-mail address removed) http://www.iviz.com/
 
T

Thomas Fritsch

Hi, I have a problem with a HashMap. I store a couple key-value of type
MyClass-Integer, but when I try to get the value, it returns null, even
if the keys seem equal. For example, suppose that MyClass has an
attribute called "name", which value is passed in the constructor, if I
do something like:

MyClass obj1 = new MyClass("MyName");
myHashMap.put(obj1, 1);

MyClass obj2 = new MyClass("MyName");
Integer value = myHashMap.get(obj2);

It should return something, and not null.
My question is, how a hashmap verifies if it contains the key "obj2"? I
suppose that it invokes the method "equals" of the class MyClass. So,
if I redefine the "equals" method in this way

public boolean equals(Object o){
if(o instanceof MyClass)
return name.equals(((MyClass)o).getName())
else
return super.equals(o);
}
Please make sure that MyClass has a hashCode() method consistent with
your equals() method. Something simple like this will probably work:
public int hashCode() {
return name.hashCode();
}
You'll find the requirements in the API doc of Object#hashCode.
 
A

andrew.tanenbaum

Thomas Fritsch ha escrito:
Please make sure that MyClass has a hashCode() method consistent with
your equals() method. Something simple like this will probably work:
public int hashCode() {
return name.hashCode();
}

Thanks to all, I overrided the hashCode method exactly like you said,
the hashmap still doesn't work :), I have to find where is the
problem, in any case now I have all the necessary knolwedge about how
hashmaps work
 
A

Alexander

Thanks to all, I overrided the hashCode method exactly like you said,
the hashmap still doesn't work :), I have to find where is the
problem, in any case now I have all the necessary knolwedge about how
hashmaps work

As far as I can see HashMap.get() does not only call the hashCode()
method of the objects, but also equals(). Hence your class should
probably not base its equals() results on actual instances but rather on
the hashCode() values.

Alexander
 
A

andrew.tanenbaum

Thomas Fritsch ha escrito:
Please make sure that MyClass has a hashCode() method consistent with
your equals() method. Something simple like this will probably work:
public int hashCode() {
return name.hashCode();
}

It doesn't work :-( For example, if I do

MyClass obj1 = new MyClass("Name");
MyClass obj2 = new MyClass("Name");
int hc1 = obj1.hashCode()
int hc2 = obj2.hashCode()

hc1 and hc2 are two different value, even if the String is the same.
 
M

Matt Humphrey

Thomas Fritsch ha escrito:


It doesn't work :-( For example, if I do

MyClass obj1 = new MyClass("Name");
MyClass obj2 = new MyClass("Name");
int hc1 = obj1.hashCode()
int hc2 = obj2.hashCode()

hc1 and hc2 are two different value, even if the String is the same.

You must have left something out. Show the exact code you're using in
MyClass.
 
A

andrew.tanenbaum

Matt Humphrey ha escrito:
You must have left something out. Show the exact code you're using in
MyClass.

I got it, it was my fault with imported jars, now it works :)
Thanks to everybody, now I know how a fu**ing hashmap works
 
T

Thomas Fritsch

Thomas Fritsch ha escrito:


Thanks to all, I overrided the hashCode method exactly like you said,
the hashmap still doesn't work :), I have to find where is the
problem, in any case now I have all the necessary knolwedge about how
hashmaps work
For me it worked. The program below printed out "value = 1" as expected.

//----------------------------------------
import java.util.HashMap;

public class MyClass {
public static void main(String[] args) {
HashMap<MyClass,Integer> myHashMap = new HashMap<MyClass,Integer>();
MyClass obj1 = new MyClass("MyName");
myHashMap.put(obj1, 1);

MyClass obj2 = new MyClass("MyName");
Integer value = myHashMap.get(obj2);
System.out.println("value = " + value);
}

private final String name;

public MyClass(String s) {
name = s;
}

public String getName() {
return name;
}

public boolean equals(Object o) {
if(o instanceof MyClass)
return name.equals(((MyClass)o).getName());
else
return super.equals(o);
}

public int hashCode() {
return name.hashCode();
}
}
//---------------------------------------------
 
P

Patricia Shanahan

Thomas Fritsch ha escrito:


It doesn't work :-( For example, if I do

MyClass obj1 = new MyClass("Name");
MyClass obj2 = new MyClass("Name");
int hc1 = obj1.hashCode()
int hc2 = obj2.hashCode()

hc1 and hc2 are two different value, even if the String is the same.

In that case, I think the problem can be safely considered to be
localized to the MyClass implementation, probably in either its
constructor or its hashCode.

You could write a very simple test program, incorporating enough of the
MyClass implementation to demonstrate the different hashCode results,
and post it.

Here's an example, but it does not reproduce the problem:

public class HashTest {
private String name;

public HashTest(String name) {
this.name = name;
}

public int hashCode() {
return name.hashCode();
}

public static void main(String[] args) {
HashTest obj1 = new HashTest("Name");
HashTest obj2 = new HashTest("Name");
int hc1 = obj1.hashCode();
int hc2 = obj2.hashCode();
System.out.printf("hc1=%d hc2=%d\n", hc1, hc2);
}
}


Patricia
 
M

Morten Alver

Alexander said:
As far as I can see HashMap.get() does not only call the hashCode()
method of the objects, but also equals(). Hence your class should
probably not base its equals() results on actual instances but rather on
the hashCode() values.

No, that is wrong. The hashCode() method is not required to return equal
values only for equal objects. Using String's hashCode() method
different Strings can return the same value from hashCode(). Your
equals() method must compare the instances. You can say that equals() is
a stronger test than hashCode().
 
A

Alexander

Morten said:
No, that is wrong. The hashCode() method is not required to return equal
values only for equal objects. Using String's hashCode() method
different Strings can return the same value from hashCode(). Your
equals() method must compare the instances. You can say that equals() is
a stronger test than hashCode().

equals() is not stronger than hashCode() as this always depends on the
implementation. String's hashCode() method actually uses the string data
to compute its hashcode and so it is not extremely likely that different
strings come out with the same hashcode (certainly it is possible as
with most hash methods).

My comment however was not in regard to string methods, but certainly in
context to MyClass. Hence it is actually correct, that equals() should
not only base its result on its object instances, but rather on the
common denominator of MyClass - which is the "name" member in this case.

Alexander
 
P

Patricia Shanahan

Alexander said:
As far as I can see HashMap.get() does not only call the hashCode()
method of the objects, but also equals(). Hence your class should
probably not base its equals() results on actual instances but rather on
the hashCode() values.

By "the hashCode() values" do you mean the fields that are used to
calculate the hash code, or the results it returns?

If the latter, you are just plain wrong, because equals is likely to
need to distinguish more than 2^32 distinct values. For example, there
are more than 2^32 sequences of 7 letters from a 26 letter alphabet.
There are going to be instances of MyClass that have the same hashCode,
but that equals should treat as being different.

If you just mean that equals and hashCode should be based on the same
set of fields, I agree, but I think your wording suggests a backwards
approach to designing them. Equality is the real issue. The hash is just
a quick check to be able to partition objects into buckets such that
objects in different buckets are definitely not equal.

First decide what makes two instances of the class equal or unequal, and
implement equals accordingly. Next, look at the fields used in the
equals method, and base the hashCode implementation on them, ensuring
that if the instances are equal they have the same hashCode.

Patricia
 
A

Alexander

Patricia said:
By "the hashCode() values" do you mean the fields that are used to
calculate the hash code, or the results it returns?

I meant to say that the class should not base its equals() result only
on comparing instances, but needs to take into account also the other
fields which make two different instances "equal". In this case
instances are not only equal if the passed object refers to the
processed one, but also when the "name" is the same.
If you just mean that equals and hashCode should be based on the same
set of fields, I agree

Exactly

Alexander
 

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

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top