Jump to content
stevenlin598

HashMap different hashcodes return different values

Recommended Posts

I have a hashmap of two objects, but it seems to return different values because the hashcode is different.

Ex.
[CODE]HashMap<HashMapTest, String> newMap = new HashMap<HashMapTest, String>();
newMap.put(new HashMapTest("test"), "line");
System.out.println(newMap.get(new HashMapTest("test")));[/CODE]
the hashcodes are different when I put it in the hashmap and when I get it from the hashmap. Is there any way of fixing this?

Share this post


Link to post
Share on other sites
[quote name='stevenlin598']I have a hashmap of two objects, but it seems to return different values because the hashcode is different.

Ex.
[CODE]HashMap<HashMapTest, String> newMap = new HashMap<HashMapTest, String>();
newMap.put(new HashMapTest("test"), "line");
System.out.println(newMap.get(new HashMapTest("test")));[/CODE]
the hashcodes are different when I put it in the hashmap and when I get it from the hashmap. Is there any way of fixing this?[/QUOTE]

What does it return?

Share this post


Link to post
Share on other sites
I havn't used Java in sooo long, but I think it's doing this because you are storing one distinct object, and expecting to get another distinct object from the map (which actually doesn't exist).

Share this post


Link to post
Share on other sites
You're creating a new instance, thus the hash code is different.
I believe you can fix that by overriding the hashCode method from the Object class, but I wouldn't advice doing that (eventhough the HashMap class protects against "bad hash codes").
Instead do it like this:

[code] HashMap<HashMapTest, String> newMap = new HashMap<HashMapTest, String>();
HashMapTest test = new HashMapTest("test");
newMap.put(test, "line");
System.out.println(newMap.get(test));[/code]

Share this post


Link to post
Share on other sites
If keys are meant to be value objects and thus lack identity, then you ought to make them reflect this by implementing the Gemini twins (equals and hashCode) to match. The reason your current solution fails is because you are presumably neither overriding hashCode() nor equals(), and thus relying on Object's default implementation which relies on [i]identity hash codes[/i] as produced by System.identityHashCode(). Instead you must implement hashCode() such that two distinct HashMapTest's with the same string will return equal hash codes.

The way a hash table works is by mapping key-value pairs to an array based on their key's hash code, so if the hash code is different, it will be unable to find the object. If you want the two keys to result in the same value when performing a table look-up, it's essential that hash code's match so that the mapping to the underlying array of pairs points to the same value.
Since hash codes can only range from -2^31 - 1 to 2^31, however, it is clear they aren't always unique, and as a result you will have collisions such that two different objects have identical hash code yet are not equal. To remedy this, you need to ensure equals() will ensure semantic equality between the two objects in addition to hashCode() so the hash table knows how to resolve this. In your case, something like this might work:
[code]
public boolean equals(Object other) {
if (other instanceof HashMapTest) {
/* compare the string values passed in our constructors,
presumably stored in a field named string. */
return ((HashMapTest) other).string.equals(this.string);
} else {
return true;
}
}
[/code]

I took the liberty to reimplement your test case in such a way to demonstrate my solution:
[code]import java.util.Map;
import java.util.HashMap;

public class TestCase {

public static void main(String[] argv) {
/* here we initialize our map and place
a value in it associated with a key */
Map<TestKey, String> map = new HashMap<>();
map.put(new TestKey(Long.MAX_VALUE), "line");

/* and now we implement the actual test
i.e. verifying that our new key with
the same value param will return the
right value from the table lookup

bonus fun fact: notice i used object identity
equality (==) to compare the string value,
rather than equals() as all string literal constants
(such as "line") are interned into a string pool
and thus only one instance exists per JVM runtime,
so this "line" and the former "line" both refer to
the same exact object */
TestKey new_key = new TestKey(Long.MAX_VALUE);
assert map.get(new_key) == "line";
}
}


class TestKey {

long value;

TestKey(long value) {
this.value = value;
}

public int hashCode() {
/* we choose to use our value itself as
our own because this ensures that two
distinct TestKeys with the same value
param have identical hash codes

note that since we're using longs
we must reduce it to its lower half
so it fits the range of hash codes */
return (int) (value & 0xffffffffL);
}

public boolean equals(Object other) {
if (other instanceof TestKey) {
/* here we compare our value to the
other's to verify they are the
same, as TestKeys are defined by
their value and not their identity */
return ((TestKey) other).value == this.value;
} else {
return true;
}
}
}[/code]

Make sure to compile using jdk7 and to run with the -ea flag:
[code]
java -ea TestCase
[/code] Edited by veer

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×