具有不正确的 equals 和 HashCode 实现的 HashMap

Dri*_*pto 2 java hash hashmap

根据我所读到的,

要使用对象作为 hashMap 的键,它必须提供正确的覆盖和实现equalshashCode 方法。HashMap get(Key k) 方法调用键对象上的 hashCode 方法,并将返回的hashValue 应用到它自己的静态哈希函数中,以找到一个存储桶位置(后备数组),其中键和值以嵌套类的形式存储,称为 Entry (Map.入口)。HashMap 的内部散列方法可以抵御低质量的散列函数。

为了测试这些契约,我编写了一个 bean 类,其中包含不正确但合法的equalshashCode方法实现。

班上:

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,HashMap的hash方法,

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

也应该每次都返回相同的值,因为哈希码总是返回 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));
    }
Run Code Online (Sandbox Code Playgroud)

输出是正确的,并显示

Item
Item1
Item2
Run Code Online (Sandbox Code Playgroud)

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

Sot*_*lis 5

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

在此处查看 Java 7 源代码(Java 8HashMap对它进行了相当大的改造,但它也做了类似的事情)

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;
}
Run Code Online (Sandbox Code Playgroud)

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