HashMap使用不正确的equals和HashCode实现

时间:2014-12-05 15:26:30

标签: java hash hashmap

根据我所读到的,

  

使用一个对象作为hashMap的关键,它必须提供正确的   覆盖和实施 等于 hashCode   方法。 HashMap get(Key k)方法在键上调用hashCode方法   对象并将返回的 hashValue 应用于其自己的静态哈希   函数找到一个桶位置(支持数组)键和   值以名为Entry(Map.Entry)的嵌套类的形式存储。   HashMap的内部哈希方法可以防止质量差的哈希值   功能

为了测试这些合同,我编写了一个bean类,其中 等于 hashCode 的错误但合法的实现方法。

班级:

public class HashVO {

    private String studentName;
    private int age;
    private boolean isAdult;

    public HashVO(String studentName, int age, boolean isAdult) {
        super();
        this.studentName = studentName;
        this.age = age;
        this.isAdult = isAdult;
    }
    public String getStudentName() {
        return studentName;
    }
    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public boolean isAdult() {
        return isAdult;
    }
    public void setAdult(boolean isAdult) {
        this.isAdult = isAdult;
    }
    @Override
    public String toString() {
        return studentName + " : " + age + " : " + isAdult;
    }
    @Override
    public boolean equals(Object obj) {
        return false;
    }
    @Override
    public int hashCode() {
        return 31;
    }

}

在这种情况下,HashMap的哈希方法,

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

每次都应返回相同的值,因为hashcode总是返回31.所以如果将HashVO类的对象用作hashMap的键,那么 get方法应该不起作用,因为它应该转到同一个桶来检索对象和equals方法总是返回false,因此它无法找到关键对象的匹配

但是当我使用这种方法时,

public static void main(String[] args) {
        HashMap<HashVO, String> voMap = new HashMap<HashVO, String>();
        HashVO vo = new HashVO("Item1", 25, true);
        HashVO vo1 = new HashVO("Item2", 12, false);
        HashVO vo2 = new HashVO("Item3", 1, false);
        voMap.put(vo, "Item");
        voMap.put(vo1, "Item1");
        voMap.put(vo2, "Item2");
        System.out.println(voMap.get(vo));
        System.out.println(voMap.get(vo1));
        System.out.println(voMap.get(vo2));
    }

输出正确,显示

Item
Item1
Item2

我想了解为什么即使Equals和HashCode方法实现不正确也会出现正确的输出。

2 个答案:

答案 0 :(得分:3)

HashMap有一个小技巧,可以在使用equals之前比较对象引用。由于您使用相同的对象引用来添加元素并检索它们,HashMap将正确返回它们。

请参阅Java 7源代码here(Java 8对HashMap进行了相当大的改进,但它做了类似的事情)

final Entry<K,V> getEntry(Object key) {
    if (size == 0) {
        return null;
    }

    int hash = (key == null) ? 0 : hash(key);
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        // HERE. Uses == with the key
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k)))) 
            return e;
    }
    return null;
}

请注意,这不是文档的一部分,因此请不要依赖它。

答案 1 :(得分:0)

HashMap的工作原理如下:

1)将保存(Key,Value)的表格单元格的索引计算为key.hashCode();

2)HashMap中的键比较equals()或参考比较。

因此,在您的情况下,所有(K,V)对都将作为LinkedList存储在HashMap表的一个单元格中。 你可以从Map获取它们,因为键的引用将等于