简体   繁体   English

自定义通用类作为哈希映射问题的关键

[英]Custom Generic Class as Key to Hash Map Issue

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. 我所做的是创建一个自定义通用类DoubleKey <K,J>来包含一个包含两部分的键。 As you can see, the Set s and the keys of HashMap hm are have the same components, but was instantiated differently ( k1 = k2 ). 正如你所看到的,集合S和HashMap HM的密钥具有相同的成分,但不同的实例化(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. 当我尝试使用s上的hm的键获取值时,它返回null ,尽管在第一次打印时它显示了正确的映射。

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: 这是我的DoubleKey实现:

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? 如果两个objecst(在这种情况下是来自自定义泛型)可以用两个相同的值实例化,则它们可以不是eve3n吗? How can I correct this? 我该如何纠正? I hope someone can help me here. 我希望有人可以在这里帮助我。 Thanks! 谢谢!

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). 除了.hashCode() ,您还应该实现equals(Object) ,而不是(仅) equals(DoubleKey<...>) ,因为否则您将在此处拥有两个独立的方法(实际上只有第一个是由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: hashCode方法也应适合此情况,例如:

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. 您的key1.toString()+key2.toString()比较有点危险,因为它使(1, 21).equals((12,1))为true,通常这是不希望的。 The same is true for your compareTo method - compare the components using their compareTo method, not the concatenated String. 对于compareTo方法也是如此-使用组件的compareTo方法(而不是串联的String)比较组件。

Learn this lesson now: If you override the equals method (as you have done), then you MUST override the hashcode method too. 现在学习本课程:如果您覆盖equals方法(如已完成),那么您也必须覆盖hashcode方法。 That method is used for various things, including looking up items in HashMaps. 该方法用于各种用途,包括在HashMaps中查找项目。

Where is DoubleKey class's hashCode method override? DoubleKey类的hashCode方法在哪里覆盖? 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. 我认为除非您实现此功能,否则它不能用作适当的键,因为否则您的两个对象将被认为是不同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM