当前位置: 首页 > 知识库问答 >
问题:

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

严天逸
2023-03-14

根据我所读到的,

要使用对象作为hashMap的键,它必须提供正确的重写和equals和hashCode方法的实现。HashMap get(Key k)方法调用Key对象上的hashCode方法,并将返回的hashValue应用到它自己的静态哈希函数中,以找到一个桶位置(备份数组),键和值以名为Entry(Map.Entry)的嵌套类的形式存储在这里。HashMap的内部哈希方法防御质量差的哈希函数。

为了测试这些契约,我编写了一个bean类,其中包含equals和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的key,那么get方法不应该起作用,因为它应该进入同一个桶来检索对象,而equals方法总是返回false,因此它将无法找到key对象的匹配项。

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方法实现不正确的情况下,仍然会出现这种正确的输出。

共有1个答案

惠文彬
2023-03-14

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

请参阅这里的Java7源代码(Java8对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;
}

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

 类似资料:
  • 问题内容: 如果我只需要比较对象并且还不打算将对象放入任何基于哈希的容器中,我是否只能实现equals()而不是hashCode()? 似乎所有Java圣经都说这两个必须一起实现。:( 我的担心:-如果我始终将hashCode()与equals()一起实现,将有很多未真正使用的代码,并且没有单元测试的内容。(如果不使用,我不会进行单元测试hashCode())-直到将对象放入基于哈希的容器中时,我

  • 我有一个hashmap有这样的键:'2+4+5','653+65+1324+75'。(整数值用+号分隔) 什么是一个好的hashcode和equals方法,使得像'2+4+5','5+4+2','4+5+2'...(所有2,4,5的排列)这样的键应该返回相同的hashcode值,而equals应该返回true。 我计划在键中取整数值,对它们进行排序,按升序将它们放入一个字符串中,并调用string

  • 如果你有如下需求,你必须重载 equals() 和 hashCode() 方法: 想把持久类的实例放入 Set 中(当表示多值关联时,推荐这么做),而且 想重用脱管实例 Hibernate 保证,仅在特定会话范围内,持久化标识(数据库的行)和 Java 标识是等价的。因此,一旦我们混合了从不同会话中获取的实例,如果希望 Set 有明确的语义,就必须实现 equals() 和 hashCode()。

  • 问题内容: 我定义了两个类,以便它们都包含对另一个对象的引用。它们看起来与此类似(这是简化的;在我的实际域模型中,类A包含一个B列表,每个B都有对父A的引用): 在与已通过使用Eclipse中A和B这两个问题的两个场产生的是调用或在任一对象方法的结果在因为它们都调用另一个对象的和方法。例如,以下程序将无法使用上述对象: 如果用这种方式用循环关系定义域模型存在内在的错误,请告诉我。据我所知,虽然这是

  • 我正在Java开发一个纸牌游戏,我试图比较hashMap中的键和ArrayList中的元素(两者都是对象)。hashCode()和equals()被重写,但出现了一个错误,我不确定它的哪一部分是错误的。 这是纸牌课 我们要做的比较 错误:点击查看错误图片

  • 问题内容: 我应该如何实施和在Java下面的类? 问题答案: 在Eclipse中,右键单击->源->生成hashCode(),然后equals()给出以下信息: 我已选择代码作为唯一字段