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

时间:2018-12-23 17:36:42

标签: java hashmap hashcode

我有一个Movie类,并且我仅覆盖了hashCode()方法。请在下面的Java类中找到

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表数组的相同索引位置中。以下是调试模式下的预期结果。

enter image description here

但是,如果我不重写hashCode()方法,而是重写equals()方法,它们将存储在HashMap表数组的相同索引位置。 尽管我可以看到哈希值不同。下面是我的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;
}

调试模式下的输出

enter image description here

如果我不重写equals和hashCode方法,那么我也会得到相同的意外结果。

据我了解,如果我不覆盖equalshashCode方法或仅覆盖equals方法,则应存储m1m2对象m1m2对象的哈希值不同,因此位置不同。但是在这种情况下,它没有发生。

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

我使用过Java 8。

2 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:2)

无论哈希代码是如何计算的,无论是通过您的方法还是通过Object类的默认值,都可以将不同的对象映射到相同的哈希图存储桶(数组索引)。哈希码除以数组大小,其余为存储桶编号。

Object.hashCode()产生的两个哈希码(31622540和27844196)在被16除(初始HashMap数组大小)时碰巧会产生相同的余数4。

因此,在有40亿个不同的哈希码可用的情况下,它们中的一些必须以相同的桶结尾,因为为每个哈希图分配40亿个元素的数组会浪费内存。

为使哈希图按预期工作,重要的是,相等的对象必须提供相同的哈希码。

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

如果您希望两部电影的场均相等,则将这两部电影设为equals(),则应像往常一样提供适当的hashCode()方法。

让我们看一下可能的替代组合。

什么都不能覆盖

两部电影都是不同的,最终会成为不同的哈希映射条目,可能在同一块中,也可能在不同的存储桶中。

仅覆盖hashCode()

这两个电影都是不同的,最终成为同一存储桶中的不同哈希映射条目。如果您仍然使用hashCode()的相等性定义,那么发明自己的Object实现毫无意义。

同时覆盖hashCode()和equals()

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

仅覆盖equals()

大错误!这两部电影是相等的,但是hashCode()的计算并没有反映出这一点,因此,是否对现有值进行搜索可以找到正确的存储区,这只是好运的问题。