Reputation: 1480
I have this following test code:
public static final String[] list = { "apple","ball","cat","dog","egg","fan","girl","hat","igloo","jerk" }; ... HashMap<DoubleKey<Integer, Integer>, String> hm = new HashMap<DoubleKey<Integer, Integer>, String>(); Set<DoubleKey<Integer, Integer>> s = new TreeSet<DoubleKey<Integer, Integer>>(); Random g = new Random(); for(int i=0; i<10; i++){ int first = g.nextInt(9999) + 1000; int second = g.nextInt(9999) + 1000; DoubleKey<Integer, Integer> k1 = new DoubleKey<Integer, Integer>(first, second); DoubleKey<Integer, Integer> k2 = new DoubleKey<Integer, Integer>(first, second); s.add(k1); hm.put(k2, list[i]); } Set<DoubleKey<Integer, Integer>> ts = hm.keySet(); Iterator<DoubleKey<Integer, Integer>> itr = ts.iterator(); while(itr.hasNext()){ DoubleKey<Integer, Integer> k = itr.next(); System.out.println(k.getFirstKey().toString() + " + " + k.getSecondKey().toString() + " -> " + hm.get(k).toString()); } System.out.println("----"); Iterator<DoubleKey<Integer, Integer>> sItr = s.iterator(); while(sItr.hasNext()){ DoubleKey<Integer, Integer> k = sItr.next(); String currStr = hm.get(k); System.out.println(k.getFirstKey().toString() + " + " + k.getSecondKey().toString() + " -> " + currStr); }
What I did is to create a Custom Generic Class DoubleKey<K, J> to contain a key having two parts. As you can see, the Set s and the keys of HashMap hm are have the same components, but was instantiated differently (k1 = k2). When I try to get a value using the keys on s to hm, it returns null, though at the first printing it shows the correct mapping.
Sample Output: 3922 + 2544 -> girl 9267 + 3750 -> hat 3107 + 10929 -> apple 5162 + 8834 -> fan 8786 + 1125 -> cat 10650 + 4078 -> egg 3808 + 7363 -> jerk 1364 + 7657 -> dog 1364 + 4412 -> ball 1583 + 1460 -> igloo ---- 10650 + 4078 -> null 1364 + 4412 -> null 1364 + 7657 -> null 1583 + 1460 -> null 3107 + 10929 -> null 3808 + 7363 -> null 3922 + 2544 -> null 5162 + 8834 -> null 8786 + 1125 -> null 9267 + 3750 -> null
This is my DoubleKey implemention:
public class DoubleKey<K extends Comparable<K>,J extends Comparable<J>> implements Comparable<DoubleKey<K,J>>{ private K key1; private J key2; public DoubleKey(K key1, J key2){ this.key1 = key1; this.key2 = key2; } public K getFirstKey(){ return this.key1; } public J getSecondKey(){ return this.key2; } // need for Comparable interface public int compareTo(DoubleKey<K,J> aThat){ // NOTE: check for nulls return (this.key1.toString() + this.key2.toString()).compareTo(aThat.key1.toString() + aThat.key2.toString()); } public boolean equals(DoubleKey<K,J> aThat){ return (this.key1.toString() + this.key2.toString()).equals(aThat.key1.toString() + aThat.key2.toString()); } }
How did it happened? Can two objecst (in this case from a custom generic) be different eve3n if they have instantiated with 2 same values? How can I correct this? I hope someone can help me here. Thanks!
Upvotes: 0
Views: 4384
Reputation: 74800
Additionally to .hashCode()
, you should have an implementation of equals(Object)
, not (only) equals(DoubleKey<...>)
, since otherwise you'll have two independent methods here (and only the first one is actually called by the HashMap). Here is a proposal:
public boolean equals(Object other) {
if(this == other)
return true;
if(!(other instanceof DoubleKey))
return false;
DoubleKey that = (DoubleKey)other;
return (this.key1 == null ? that.key1 == null : this.key1.equals(that.key1)) &&
(this.key2 == null ? that.key2 == null : this.key2.equals(that.key2));
}
The hashCode method should be made to fit this, too, for example like this:
public int hashCode() {
return key1.hashCode() * 3 + key2.hashCode() * 5;
}
Your key1.toString()+key2.toString()
comparison is a bit dangerous, as it lets (1, 21).equals((12,1))
be true, which is usually not intended. The same is true for your compareTo method - compare the components using their compareTo method, not the concatenated String.
Upvotes: 4
Reputation: 285430
Where is DoubleKey class's hashCode method override? I don't think that it will work as a proper key unless you implement this because otherwise your two objects will be considered different.
Upvotes: 1
Reputation: 24370
Learn this lesson now: If you override the equals method (as you have done), then you MUST override the hashcode method too. That method is used for various things, including looking up items in HashMaps.
Upvotes: 3