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

编译器或JVM是否强制执行hashCode/equals契约?

钱和安
2023-03-14

我在HashMap中存储的对象作为键覆盖equals(),但不是hashCode();当我在映射中放置一个对象时,equals()方法没有被调用。如果我还重写hashCode(),则会调用equals()方法。为什么?

为什么我不能使用自定义的equals方法来阻止向映射中添加对象,而不管我是否重写hashCode()?

谢谢

package package1;

public class PlayWithHash {

    public static void main(String [] args){
        java.util.Map<Cat, Cat> map = new java.util.HashMap<Cat, Cat>();
        map.put(new Cat(), new Cat());
        map.put(new Cat(), new Cat());
        System.out.println("hashmap size = " + map.size());
    }
}

class Cat{
    @Override
    public int hashCode(){
        return 1;
    }
    @Override
    public boolean equals(Object d){
        Cat c = (Cat) d;
        if(this.hashCode() == c.hashCode()){
            return true;
        }
        return false;
    }
}

如果注释了hashCode(),则大小为2,否则大小为1。

我在想,如果我不重写hashCode(),而是使用对象的hashCode(),HashMap将“看到”我的两个对象的hashCode是不同的,并得出结论,没有必要调用equals()。如果这是真的,HashMap至少是合同的一部分。这可能是HashMap优化的另一种表现形式。

但是,您(greenSocksRock)写道,“……密钥的hashCode()实现没有为不同的密钥返回足够不同的哈希值。”请详细说明一下好吗?

谢谢

PS抱歉,我仍在试图找到解决StackOverflow编辑问题的方法。

共有3个答案

微生嘉
2023-03-14

hashmap在内部实现为链接列表的集合。搜索对象(使用传递的键)的常用机制是首先对键进行散列,然后遍历链表中与散列值对应的各个对象。

根据相同的逻辑,对于HashMap中的对象,只有当hashCode()方法为两个对象返回相同的哈希值时,才会调用equals()方法。

因此,在实现自定义类时,建议不仅覆盖equals()方法,还覆盖hashCode()方法。

戎永福
2023-03-14

恐怕编者没有强制执行。为了更好地理解这个话题,请阅读本文http://tutorials.jenkov.com/java-collections/hashcode-equals.html

柳坚白
2023-03-14

编译器和JVM都不执行hashCode/equals契约。合同是在对象和你的类之间签订的,也就是说,你有责任遵守并执行该合同!

在Java中,对象类列出了的契约。等于()。hashCode(),这是大多数基于哈希的标准库类所依赖的。现在,如果您实现自己的类,它会隐式地扩展对象,因此,如果您希望自己的类与依赖该契约的其他类正确工作,您应该在自己的实现中强制执行通用契约。

作为提醒,从对象文档中:

如果根据equals(Object)方法,两个对象相等,那么对这两个对象中的每一个调用hashCode方法必须产生相同的整数结果。

在没有看到您的实现的情况下,我只能猜测为什么在您将对象放入Map时调用equals(),我的猜测是您的键的hashCode()实现没有为不同的键返回足够不同的哈希值。
例如,当您将某些东西放入HashMap时,它会调用key的hashCode()方法,以找到一个位置来存储您的值。如果该位置已经包含一个值,则可能会调用key的equals()方法来确定下一步要做什么。

我不确定我是否理解你在最后一个问题中试图做什么,当你说你想“阻止向地图添加对象”时。如果您愿意的话,您肯定可以使用自定义的equals实现来实现这一点。看看地图。containsKey()映射。containsValue(),根据您的情况,在将新条目放入地图之前。

最后,如果可以的话,我建议大家看看Joshua Bloch的《有效Java,第二版》中的第8项和第9项,以获得更深入的解释。

 类似资料:
  • 我想你们大多数人都知道在Java语言中是一个保留的关键字,但实际上并没有被使用。你们可能也知道是一个Java虚拟机(JVM)操作码。我认为Java、Scala和静态编程语言的所有复杂的控制流结构都是在JVM级别上使用和、、等的某种组合来实现的。 查看JVM规范https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.

  • 在我的所有项目中,我都使用gradle并指定以下内容: 现在,我安装了三个不同版本的JDK,从1.6到1.8。为了从一个版本切换到另一个版本,Ishell文件更改、甚至。 偶然的是,我可能会使用错误的JDK版本,而我不希望这样...是否有可能在尝试任何编译任务之前检查编译器版本是否等于targetCompatibility?

  • 问题内容: 我对JVM有一个非常基本的问题:它是编译器还是解释器? 如果它是解释器,那么JVM内部存在的JIT编译器怎么办? 如果两者都不是,那么JVM到底是什么?(我不希望将字节码转换为机器特定的代码等jVM的基本定义。) 问题答案: 首先,让我们对以下术语有一个清晰的认识 是Java编译器-将Java代码编译为 Bytecode 是Java虚拟机-运行/解释/将字节码转换为本 机代码 是即时编

  • 问题内容: 我很好奇JVM的工作方式。JVM是否承认诸如“专用”之类的方法可访问性规则受保护,还是仅在编译时完成? 例如,是否可以在第37行附近进行一些字节码操作并调用一个受保护的方法,例如test3?通常,编译器不会让我调用该方法,因为它被声明为protected。但是我很好奇,是否在运行时强制执行了该受保护的规则? u.test1(); //在运行时是否可以通过字节码操作来调用“ test3”

  • 我试图理解Java的关键字,关于在具有CPU缓存的多线程程序中写入易失性原子变量。 我已经阅读了一些教程和Java语言规范,特别是关于“订购前发生”的第17.4.5节。我的理解是,当一个线程将一个新值写入一个可变变量时,更新后的值必须对读取该变量的其他线程可见。对我来说,这些语义可以通过以下两种方式之一实现: > 线程可以在CPU缓存中缓存易失性变量,但对缓存中变量的写入必须立即刷新到主内存。换句

  • 问题内容: and方法必须一致,这意味着当两个对象根据方法相等时,它们的方法应返回相同的哈希值。 如果我们不重写hashCode()方法,则Java将返回唯一的哈希码。 为什么取消注释该行会导致编译错误? 如果对象的哈希码不相等,即使默认哈希码有所不同,为什么它们显示为相等? 问题答案: 平等仅由方法equals()确定。并且hashCode()方法可用于其他情况,例如Map或Set。实际调用eq