如果我重写了 Java 中的 equals 方法,为什么还需要重写 hashcode?

2023-12-25

我知道每当equals方法在 Java 中被重写。那只是一份合同。我试图理解这背后的逻辑。我正在阅读*Effective Java约书亚·布洛赫 https://en.wikipedia.org/wiki/Joshua_Bloch,我遇到了这段代码(第 9 项,第 45 页):

import java.util.HashMap;
import java.util.Map;

public final class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode, int prefix, int lineNumber) {
        rangeCheck(areaCode, 999, "area code");
        rangeCheck(prefix, 999, "prefix");
        rangeCheck(lineNumber, 9999, "line number");
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }

    private static void rangeCheck(int arg, int max, String name) {
        if (arg < 0 || arg > max)
            throw new IllegalArgumentException(name + ": " + arg);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof PhoneNumber))
            return false;
        PhoneNumber pn = (PhoneNumber) o;
        return pn.lineNumber == lineNumber && pn.prefix == prefix
                && pn.areaCode == areaCode;
    }

    // Broken - no hashCode method!

    // A decent hashCode method - Page 48
    // @Override public int hashCode() {
    // int result = 17;
    // result = 31 * result + areaCode;
    // result = 31 * result + prefix;
    // result = 31 * result + lineNumber;
    // return result;
    // }

    // Lazily initialized, cached hashCode - Page 49
    // private volatile int hashCode; // (See Item 71)
    //
    // @Override public int hashCode() {
    // int result = hashCode;
    // if (result == 0) {
    // result = 17;
    // result = 31 * result + areaCode;
    // result = 31 * result + prefix;
    // result = 31 * result + lineNumber;
    // hashCode = result;
    // }
    // return result;
    // }

    public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
        m.put(new PhoneNumber(707, 867, 5309), "Jenny");
        System.out.println(m.get(new PhoneNumber(707, 867, 5309)));
    }
}

这是他在文中提到的,我很难理解。

此时,您可能会期望m.get(new PhoneNumber(707, 867, 5309))返回“Jenny”,但它返回 null。请注意,两个 涉及到 PhoneNumber 实例:一个用于插入到 HashMap 和第二个相等的实例用于(尝试) 恢复。 PhoneNumber类无法覆盖hashCode的原因 两个相等的实例具有不相等的哈希码,违反了 哈希码合约。因此 get 方法可能会寻找 电话号码与原来的哈希桶位于不同的哈希桶中 通过 put 方法存储

我不明白他所说的两个 PhoneNumber 实例是什么。只有我创建的实例m.put(new PhoneNumber(707, 867, 5309), "Jenny")。另外,我再次查找该对象,即使它继承了对象类的 hashCode 方法,它也应该返回相同的哈希码。 为什么会出现这种情况?这里的一些解释会有很大帮助。


我不明白他所说的两个 PhoneNumber 实例是什么。

第一个是您用于插入的。

m.put(new PhoneNumber(707, 867, 5309), "Jenny"));

第二个实例是用于检索的实例。

m.get(new PhoneNumber(707, 867, 5309)); // Notice the use of "new"

另外,我再次查找该对象,即使它继承了对象类的 hashCode 方法,它也应该返回相同的哈希码。

这是不正确的。默认实现hashCode() in Object类为不同的对象返回不同的整数,因为它是通过将对象的内部地址转换为整数来实现的。因此,哈希码检查在那里失败。

另一方面,如果您尝试检索PhoneNumber使用同一个实例

PhoneNumber phoneNum = new PhoneNumber(707, 867, 5309);
m.put(phoneNum, "Jenny"));
m.get(phoneNum); // Not NULL

哈希码检查将通过(因为您使用了之前插入的相同对象)并且equals()以及。这当然不是推荐的方法,因为我们更有可能使用与插入所用的不同的关键对象。

然而,所使用的键在意义上是等效的(就像不同的 String 对象,但其文本相同),因此提供了一个hashCode()为了正确进行匹配和检索,需要实施。

另请参阅:检查和删除 Java HashMap 中的元素 https://stackoverflow.com/q/17684341/1237040

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果我重写了 Java 中的 equals 方法,为什么还需要重写 hashcode? 的相关文章

随机推荐

  • 如何改变约束

    SQL 如何更改约束 下面是我的约束之一 CONSTRAINT ACTIVEPROG FKEY1 FOREIGN KEY ActiveProgCode REFERENCES PROGRAM ActiveProgCode 我想添加 ON DE
  • 这段代码片段中是如何进行递归的?

    看看调用堆栈和监视 现在当我按 F10 时 监视 中的行值更改为 对象对象 因此一切都发生了 直到此时我才理解流程 之后发生的事情不清楚 现在 为什么即使我在第 8 行提供断点 代码也不会在第 8 行停止 This is the quest
  • 在 REST 中,为每个资源表示创建单个内容类型还是具有单独的内容类型?

    如果我想遵循为 REST API 使用自定义内容类型的做法 我是否应该为整个项目定义单个自定义内容类型 或者为每个资源表示 发送到 返回的内容 定义自定义内容类型 REST API 在我的项目中使用 也就是说 如果我正在构建一个 Books
  • 在 ReactJS 中处理未定义值的最佳方法?

    我正在使用 ReactJS 访问 API 当 React 组件访问 API 提供的对象中可能 未定义 的属性时 阻止 React 组件崩溃的最佳方法是什么 错误的一个例子是 类型错误 无法读取未定义的属性 项目 您似乎正在尝试访问该属性it
  • 运行鼻子测试时出错

    我最近重新安装了nose对我的代码运行测试 https nose readthedocs org en latest https nose readthedocs org en latest I used sudo pip install
  • Cordova / Ionic:在设备上模拟或运行时未处理 $http 请求

    上周一切都很顺利 当我在设备上运行应用程序或使用 Genymotion 进行模拟时 对 api 的所有调用都正常工作 要么返回数据 要么失败 但至少显示一些内容 我正在使用 ionic run android 我添加更新全局科尔多瓦离子 n
  • 如何禁用 AdMob 日志?

    我知道发布带有日志的应用程序不是一个好的做法 因此我禁用了所有日志调用 现在 AdMob 每次显示广告时都会记录日志 我该如何取消呢 我似乎在文档中找不到有关此主题的任何信息 并且我读到您可以使用ProGuard http proguard
  • 如何屏蔽 cpu 不受 linux 调度程序的影响(防止它调度线程到该 cpu 上)?

    可以使用sched setaffinity将线程固定到 CPU 提高性能 在某些情况下 来自 Linux 手册页 限制进程在单个 CPU 上运行还可以避免 缓存失效导致的性能成本 进程停止在一个 CPU 上执行 然后在另一个 CPU 上重新
  • cverror android 断言失败 (scn == 3) Android

    我正在尝试对 Android 中的图像 位图 使用自适应阈值 这需要将其更改为 Mat 然后将其转换为灰度 下面是我从创建位图开始的代码 它们被命名为裁剪 因为我刚刚裁剪了之前的照片 Bitmap bmCrop BitmapFactory
  • 检测网络浏览器是否重定向

    我正在寻找一种方法 WebBrowser 实例可以检测当前页面是否要重定向 或者 如果在页面最初加载时弹出广告 然后转到实际的 URL 我正在寻找一种方法来检测它 有人有什么想法吗 Thanks 您可以使用 WebBrowser 控件的 N
  • PDO多过滤sql查询

    有没有一种更简单的方法可以让我编写代码并执行相同的操作 而不必进行这么多查询 我正在尝试添加分页 此处未包含在代码中 它适用于我的 ALL 查询 但 AND OR 查询给出了有趣的结果 并且变得令人头疼 除此之外 结果过滤工作完美 只需要分
  • Android Studio:添加文件后如何“刷新”?

    我正在尝试向 Android 项目添加布局 右键单击了res folder New Directory 该目录已创建 但未显示在app res 也许是因为默认情况下隐藏空目录 转到 Windows 资源管理器并将文件添加到新创建的目录 隐藏
  • 更改子列表中的字符

    我已经知道这是一个非常愚蠢的问题 我尝试查找答案 但我几乎不知道该问什么 抱歉 如果标题有点模糊 但我走了 我有一个单词列表 我想去掉列表中的坏角色 List I Can Not Do It BadChars for word in Lis
  • 如何开始使用 dockerode

    我计划在 docker 中运行我的应用程序 我想在 docker 容器上动态启动 停止 构建 运行命令 我发现了一个名为 dockerode 的工具 Here https github com apocas dockerode是项目存储库
  • 有没有办法在 J2EE 服务器启动上做一些事情?我正在使用 Tomcat [重复]

    这个问题在这里已经有答案了 我正在尝试让 J2EE 服务器主动向另一台服务器注册 即发送一些消息 而不是作为对某些内容的响应 令人惊讶的是 我发现很少有关于是否有事件和 或类可以扩展的信息或问题 这些事件和 或类可以让我处理 服务器启动 我
  • 如何解决“错误:scalac:类文件‘package.class’中遇到对 scala.ScalaObject 的错误符号引用”?

    当我尝试运行该项目时 我遇到此错误消息 Error scalac bad symbolic reference to scala ScalaObject encountered in class file package class Can
  • 在数组上调用 .length 与保存大小变量的性能有区别吗?

    我正在创建一个模拟程序 我希望代码能够得到非常优化 现在我有一个数组 它在我使用的各种 for 循环中进行了多次循环 for int i 0 i
  • ') 和 $('p') 有什么区别' aria-label='jQuery $('

    ') 和 $('p') 有什么区别'> jQuery $('

    ') 和 $('p') 有什么区别

    有人可以向我解释一下两者之间的区别吗 p and p 在 jQuery 中 例如 如果我写 body append p p html hello my friend 然后出现文本 你好我的朋友 然而 当我写 body append p ht
  • 从 C# 中的自定义字段属性获取值

    今天早上 我开始了我认为使用自定义字段属性的快速练习 在尝试了很多事情并搜索了很多示例 大多数涉及类而不是字段属性 之后 我正式陷入困境 我的代码如下 一个特点是该类是使用类构建器在 FileHelpers 中构建的 我的各种部分成功的尝试
  • 如果我重写了 Java 中的 equals 方法,为什么还需要重写 hashcode?

    我知道每当equals方法在 Java 中被重写 那只是一份合同 我试图理解这背后的逻辑 我正在阅读 Effective Java约书亚 布洛赫 https en wikipedia org wiki Joshua Bloch 我遇到了这段