当前位置: 首页 > 面试题库 >

在Java中,为什么equals()和hashCode()必须一致?

刘绍晖
2023-03-14
问题内容

如果我重写一个类两种方法,它必须确保,如果A.equals(B) == true那么A.hashCode() == B.hashCode也必须是真实的。

有人可以告诉我一个简单的示例,如果违反了该示例,将会引起问题吗?我认为这与您使用该类作为Hashmap的键类型有关吗?


问题答案:

当然:

public class Test {
  private final int m, n;

  public Test(int m, int n) {
    this.m = m;
    this.n = n;
  }

  public int hashCode() { return n * m; }

  public boolean equals(Object ob) {
    if (ob.getClass() != Test.class) return false;
    Test other = (Test)ob;
    return m == other.m;
  }
}

与:

Set<Test> set = new HashSet<Test>();
set.put(new Test(3,4));
boolean b = set.contains(new Test(3, 10)); // false

从技术上讲应该是正确的,因为在两种情况下m == 3。

通常,HashMap的工作方式如下:它具有可变数量的通常称为“存储桶”的数量。存储桶的数量可以随时间变化(随着条目的添加和删除),但始终为2的幂。

假设一个给定的HashMap有16个存储桶。调用put()添加条目时,将计算密钥的hashCode(),然后根据存储桶的大小采用掩码。如果您(按位)将hashCode()与15(0x0F)进行与,您将获得最后4位,等于0到15之间(包括0和15)的数字:

int factor = 4;
int buckets = 1 << (factor-1) - 1; // 16
int mask = buckets - 1; // 15
int code = key.hashCode();
int dest = code & mask; // a number from 0 to 15 inclusive

现在,如果该存储桶中已经有一个条目,那么您将拥有一个 碰撞 。有多种处理方法,但是HashMap使用的方法(可能是最常见的方法)是存储
。具有相同被屏蔽的hashCode的所有条目都放在某种列表中。

因此,查找给定键是否已在地图中:

  1. 计算被屏蔽的哈希码;
  2. 找到合适的桶;
  3. 如果为空,则找不到密钥;
  4. 如果不为空,则循环遍历存储桶中的所有条目,检查equals()。

通过存储桶查看是线性(O(n))操作,但它位于较小的子集上。哈希码桶确定基本上是常数(O(1))。如果存储桶足够小,则通常将对HashMap的访问描述为“在O(1)附近”。

您可以对此进行一些观察。

首先,如果您有一堆全部返回42作为其哈希码的对象,a
HashMap仍然可以工作,但它将作为昂贵的列表运行。访问将是O(n)(因为所有内容都将在同一个存储桶中,而不管存储桶的数量如何)。实际上在面试中有人问过我。

其次,回到原来的观点,如果两个对象相等(意思是a。equals(b) == b.equals(a) == true)但具有不同的哈希码,HashMap则将(可能)查找错误的存储桶,从而导致不可预测和不确定的行为。



 类似资料:
  • 本文向大家介绍hashCode 与 equals ?为什么重写equals时必须重写hashCode方法?相关面试题,主要包含被问及hashCode 与 equals ?为什么重写equals时必须重写hashCode方法?时的应答技巧和注意事项,需要的朋友参考一下 面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?” ha

  • 问题内容: 决定将这些方法包含在java.lang.Object中的背后原因是什么?平等和哈希对于许多类没有意义。 建立两个接口将更加合乎逻辑: 例如,HashSet定义可能看起来像 这将防止出现一个常见的初学者错误-使用项目集而不实现equals / hashCode。 问题答案: 当我们实现一个接口时,我们注入(或接受)该接口定义的合同。 &是两个不同的合同。但是,如果我们仔细观察,就会发现它

  • 本文向大家介绍为什么在重写 equals方法的同时必须重写 hashcode方法,包括了为什么在重写 equals方法的同时必须重写 hashcode方法的使用技巧和注意事项,需要的朋友参考一下 我们都知道Java语言是完全面向对象的,在java中,所有的对象都是继承于Object类。 其 equals 方法比较的是两个对象的引用指向的地址,hashcode 是一个本地方法,返回的是对象地址值。O

  • 问题内容: 在java中为什么需要覆盖equals和hashcode方法?什么时候用到? 问题答案: 让我们尝试通过一个示例来理解它,如果我们不进行覆盖而覆盖并尝试使用。 假设我们有一个类像这样那样的两个对象是相等的,如果他们等于(和生成) 仅覆盖 如果仅覆盖被覆盖,则在你第一次调用时将散列到某个存储桶,而在调用时将散列到其他存储桶(因为它们具有不同的)。因此,尽管它们是相等的,但由于它们没有散列

  • 问题内容: 无论是Javadoc还是代码本身,Comparator接口都定义了: 但这没有编译任何概率: 但这确实是: 接口不允许用户重写方法的方法是什么? 问题答案: 首先,JavaDocs清楚地解释了您应该实现此方法: 此外,仅当指定对象也是一个比较器并且施加与该比较器相同的顺序时,此方法才能返回true。因此,意味着对于每个对象引用和。 但后来: 请注意,始终不要覆盖即可。 即使它是接口的一

  • 本文向大家介绍java中为何重写equals时必须重写hashCode方法详解,包括了java中为何重写equals时必须重写hashCode方法详解的使用技巧和注意事项,需要的朋友参考一下 前言 大家都知道,equals和hashcode是java.lang.Object类的两个重要的方法,在实际应用中常常需要重写这两个方法,但至于为什么重写这两个方法很多人都搞不明白。 在上一篇博文Java中e