Java 虽然散列值不同,但为什么我的对象存储在同一位置?

Java 虽然散列值不同,但为什么我的对象存储在同一位置?,java,hashmap,hashcode,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

我有一个
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");

}

}
调试模式下的输出

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

根据我的理解,如果我不重写
equals
hashCode
方法,或者只重写
equals
方法,那么
m1
m2
对象应该存储在不同的位置,因为
m1
m2
对象的哈希值不同。但在这种情况下,它不会发生

有人能解释一下为什么用不同的散列值,我的对象存储在同一个位置吗


我使用过Java 8。

哈希代码的范围非常大,从
Integer.MIN\u值
Integer.MAX\u值
,而
HashMap
通常具有更少的存储桶(默认情况下,新实例化的
HashMap
,至少在OpenJDK 11中是16个)。因此,散列代码完全有可能发生冲突,多个对象将被添加到同一个bucket中,这甚至是意料之中的。但是,请注意,如果您没有重写
hashCode()
,则此行为完全是偶然的,不能依赖。

无论哈希代码是如何计算的,通过您的方法还是默认情况下从
对象
类,不同的对象都可以映射到相同的hashmap bucket(数组索引)。散列码除以数组大小,余数给出桶号

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

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

要使哈希映射按预期工作,重要的是相等的对象提供相同的哈希代码

如果只重写
equals()
方法,则
对象.hashCode()
无法满足该要求,您还必须重写
hashCode()
——否则
get()
方法将找不到存储在映射中的对象

如果希望两部电影的字段相等,则应提供适当的
hashCode()
方法

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

不凌驾 这两部电影都是不同的,结果是不同的散列映射条目,可能在相同的,可能在不同的存储桶中

仅重写hashCode() 这两部电影是不同的,在同一个bucket中以不同的哈希映射条目结束。如果仍然使用
对象
平等定义,那么发明自己的
hashCode()
实现是毫无意义的

重写hashCode()和equals() 这两部电影都是相同的,最终只有一个散列映射条目,后面存储的值获胜。发生这种情况的原因是,第二个
put()
在哈希代码的bucket下找到一个具有相等键的条目,并简单地替换其值部分

仅覆盖等于() 大错特错!这两部电影都是相同的,但这并没有反映在
hashCode()
计算中,因此搜索现有值是否查找到正确的bucket只是运气的问题

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