如果我重写了 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? 的相关文章

  • Java中字符串中特殊字符的替换

    Java中如何替换字符串 E g String a adf sdf 如何替换和避免特殊字符 您可以删除除此之外的所有字符可打印的 ASCII 范围 http en wikipedia org wiki ASCII ASCII printab
  • Java:如何从转义的 URL 获取文件?

    我收到了一个定位本地文件的 URL 事实上我收到的 URL 不在我的控制范围内 URL 按照 RFC2396 中的定义进行有效转义 如何将其转换为 Java File 对象 有趣的是 URL getFile 方法返回一个字符串 而不是文件
  • org.apache.sling.api.resource,version=[2.3,3) -- 无法解析

    您好 我无法访问我的项目内容 我已经上传了从 CQ 访问内容所需的所有包 我唯一能看到的是 org apache sling api resource version 2 3 3 无法解析 这是否是异常的原因 如果是 请告诉我如何解决 中Q
  • Android在排序列表时忽略大小写

    我有一个名为路径的列表 我目前正在使用以下代码对字符串进行排序 java util Collections sort path 这工作正常 它对我的 列表进行排序 但是它以不同的方式处理第一个字母的情况 即它用大写字母对列表进行排序 然后用
  • 如何使用 Java 处理 Selenium WebDriver 中的新窗口?

    这是我的代码 driver findElement By id ImageButton5 click Thread sleep 3000 String winHandleBefore driver getWindowHandle drive
  • 运行具有外部依赖项的 Scala 脚本

    我在 Users joe scala lib 下有以下 jar commons codec 1 4 jar httpclient 4 1 1 jar httpcore 4 1 jar commons logging 1 1 1 jar ht
  • JavaFX 中具有自定义内容的 ListView

    How i can make custom ListView with JavaFx for my app I need HBox with image and 2 Labels for each line listView 您可以通过查看
  • 如何在不超过最大值的情况下增加变量?

    我正在为学校开发一个简单的视频游戏程序 我创建了一个方法 如果调用该方法 玩家将获得 15 点生命值 我必须将生命值保持在最大值 100 并且由于我目前的编程能力有限 我正在做这样的事情 public void getHealed if h
  • 我可以使用子接口重新编译公共 API 并保持二进制兼容性吗?

    我有一个公共 API 在多个项目中多次使用 public interface Process
  • 使用 AES SecretKey 的 Java KeyStore setEntry()

    我目前正在 Java 中开发一个密钥处理类 特别是使用 KeyStore 我正在尝试使用 AES 实例生成 SecretKey 然后使用 setEntry 方法将其放入 KeyStore 中 我已经包含了代码的相关部分 The KS Obj
  • 画透明圆,外面填充

    我有一个地图视图 我想在其上画一个圆圈以聚焦于给定区域 但我希望圆圈倒转 也就是说 圆的内部不是被填充 而是透明的 其他所有部分都被填充 请参阅这张图片了解我的意思 http i imgur com zxIMZ png 上半部分显示了我可以
  • hibernate锁等待超时超时;

    我正在使用 Hibernate 尝试模拟对数据库中同一行的 2 个并发更新 编辑 我将 em1 getTransaction commit 移至 em1 flush 之后我没有收到任何 StaleObjectException 两个事务已成
  • 具有 java XSLT 扩展的数组

    我正在尝试使用 java 在 XSLT 扩展中使用数组 我收到以下错误 Caused by java lang ClassCastException org apache xpath objects XObject cannot be ca
  • 如何正确取消引用然后删除 JavaScript 对象?

    我想知道从内存中完全取消引用 JavaScript 对象的正确方法 确保删除时不会在内存中悬空 并且垃圾收集器会删除该对象 当我看这个问题时在 JavaScript 中删除对象 https stackoverflow com questio
  • react-native run-android 失败并出现错误:任务 ':app:dexDebug' 执行失败

    我使用的是 Windows 8 1 和react native cli 1 0 0 and react native 0 31 0 添加后react native maps对于该项目 我运行了命令react native upgrade并给
  • 如何在selenium服务器上提供自定义功能?

    我知道可以通过某种方法获得一些硒功能 其中之一如下 driver getCapabilities getBrowserName 它返回浏览器名称的值 但如果它指的是一个可用的方法 如果我没有误解的话 这似乎与自定义功能有关 就像我的意思是
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • javafx android 中的文本字段和组合框问题

    我在简单的 javafx android 应用程序中遇到问题 问题是我使用 gradle javafxmobile plugin 在 netbeans ide 中构建了非常简单的应用程序 其中包含一些文本字段和组合框 我在 android
  • hashcode 的默认实现为以相同方式构造的对象返回不同的值

    我在这里编写一个示例代码 public class Test private int i private int j public Test TODO Auto generated constructor stub public Test
  • 调整添加的绘制组件的大小和奇怪的摆动行为

    这个问题困扰了我好几天 我正在制作一个特殊的绘画程序 我制作了一个 JPanel 并添加了使用 Paint 方法绘制的自定义 jComponent 问题是 每当我调整窗口大小时 所有添加的组件都会 消失 或者只是不绘制 因此我最终会得到一个

随机推荐

  • 如何改变约束

    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 我遇到了这段