虽然哈希值不同,但为什么我的对象存储在同一个位置?

Although hash values are different, still why are my objects stored in the same location?

我有一个 Movie class 并且 我只覆盖 hashCode() 方法 。请在下面找到 java class

public class Movie {

private String actor;
private String name;
private String releaseYr;

public String getActor() {
    return actor;
}

public void setActor(String actor) {
    this.actor = actor;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getReleaseYr() {
    return releaseYr;
}

public void setReleaseYr(String releaseYr) {
    this.releaseYr = releaseYr;
}
@Override
    public int hashCode() {
        return actor.hashCode() + name.hashCode() + releaseYr.hashCode();
    }


}

我创建了两个 Movie 对象,两个对象的所有 属性 值都相同,并将它们放在 HashMap 中。下面是代码

import java.util.HashMap;

public class Test {

public static void main(String[] args) {

    Movie m1 = new Movie();
    m1.setActor("Akshay");
    m1.setName("Taskvir");
    m1.setReleaseYr("2010");

    Movie m2 = new Movie();
    m2.setActor("Akshay");
    m2.setName("Taskvir");
    m2.setReleaseYr("2010");


    HashMap<Movie, String> map = new HashMap<Movie, String>();

    map.put(m1, "Value of m1");
    map.put(m2, "Value of m2");

}

}

我得到了预期的结果。由于我只覆盖了 hashCode() 方法,而且对象的 哈希值相同,所以它们存储在 HashMap table 数组的相同索引位置 中。下面是调试模式下的预期结果。

但是如果我不覆盖 hashCode() 方法而是覆盖 equals() 方法,它们将存储在 HashMap table 数组的相同索引位置。 尽管我可以看到哈希值不同。下面是我的 equals 方法

@Override
public boolean equals(Object obj) {

    Movie m1 = (Movie) obj;
    boolean result = false;

    if (m1.getActor().equals(this.actor) && m1.getName().equals(this.name)
            && m1.getReleaseYr().equals(this.releaseYr)) {
        result = true;
    }

    return result;
}

调试模式下的输出

如果我不覆盖 equals 和 hashCode 方法,那么我也会得到同样的意外结果。

根据我的理解,如果我不覆盖 equalshashCode 方法或只覆盖 equals 方法,那么 m1m2 对象应该存储在不同的位置,因为 m1m2 对象的哈希值不同。但在这种情况下,它没有发生。

有人可以解释一下为什么我的对象存储在相同位置的哈希值不同吗?

我用过Java8.

哈希码有一个 巨大的 范围,从 Integer.MIN_VALUEInteger.MAX_VALUE,而 HashMap 通常有更少的桶(默认情况下, 16 对于新实例化的 HashMap,至少对于 OpenJDK 11)。因此,哈希码发生冲突是完全有可能的,甚至是意料之中的,并且多个对象将被添加到同一个桶中。但是,请注意,如果您没有覆盖 hashCode(),此行为完全是偶然的,不能依赖。

无论哈希代码是如何计算的,通过您的方法或默认来自 Object class,不同的对象都可以映射到相同的哈希映射桶(数组索引)。哈希码除以数组大小,余数给出桶号

Object.hashCode() 生成的两个哈希码(31622540 和 27844196)在除以 16(初始 HashMap 数组大小)时恰好生成相同的余数 4。

因此,有 40 亿个不同的哈希码可用,其中一些必须在同一个桶中结束,因为为每个哈希映射分配一个 40 亿个元素的数组会浪费内存。

要使哈希映射按预期工作,重要的是相等的对象给出相同的哈希码。

如果您仅覆盖 equals() 方法,则 Object.hashCode() 不满足该要求,您还必须覆盖 hashCode() - 否则 get()方法将找不到您存储在地图中的对象。

如果你想让两部电影 equals() 如果它们的字段相等,你应该提供一个合适的 hashCode() 方法,就像你做的那样。

让我们看看可能的覆盖组合。

不覆盖任何内容

两部电影不同,最终作为不同的哈希映射条目,可能在同一个桶中,也可能在不同的桶中。

只覆盖 hashCode()

两部电影不同,最终作为同一存储桶中的不同哈希映射条目。如果您仍然使用相等的 Object 定义,那么发明您自己的 hashCode() 实现是无稽之谈。

覆盖 hashCode() 和 equals()

两部电影相等,最终只有一个哈希映射条目,后存储的值获胜。发生这种情况是因为第二个 put() 在哈希码的桶下找到了一个具有相同键的条目,并简单地替换了它的值部分。

只覆盖 equals()

大错!两部电影是相等的,但这并没有反映在 hashCode() 计算中,所以对现有值的搜索是否找到正确的桶只是运气问题。