行业资讯 2025年08月6日
0 收藏 0 点赞 676 浏览 2869 个字
摘要 :

文章目录 1. 键必须遵守hashCode()和equals()之间的契约 2. 如果允许更改键的hashCode会发生什么? 3. 我们应该使HashMap的键不可变 4. 自定义键的HashMap示例 5. 示例 ……




  • 1. 键必须遵守hashCode()和equals()之间的契约
  • 2. 如果允许更改键的hashCode会发生什么?
  • 3. 我们应该使HashMap的键不可变
  • 4. 自定义键的HashMap示例
  • 5. 示例
  • 6. 结论

在Java中,我们可以使用对象作为HashMap的key,但在这样做时需要考虑重要的因素和准则。这的确是一个常见的面试问题,需要对HashMap的工作原理有很好的理解。以下是用户定义的类用作HashMap中的键时,需要考虑的几个重要因素:

1. 键必须遵守hashCode()和equals()之间的契约

为了设计一个良好的键,基本需求是“我们应该能够在不失败的情况下从map中检索值对象”,否则无论你构建多么复杂的数据结构,都将毫无用处。

为了决定我们是否已经创建了一个良好的键,我们必须了解“HashMap的工作原理”。“HashMap的工作原理”部分请自行阅读相关文章,但总结来说,它是基于哈希的原则工作的。

在HashMap中,键的hashCode()主要与equals()方法一起使用,用于将键放入map中,然后从map中获取它。因此,我们的唯一关注点是这两个方法。

  • equals() – 验证两个对象的相等性,这在我们的案例中是键。要重写以提供比较两个键的逻辑。
  • hashCode() – 在运行时返回键的唯一整数值。这个值用于查找map中的桶位置。

通常,如果重写equals()方法,则必须重写hashCode()方法,以保持hashCode()方法的一般合同,该合同规定相等的对象必须具有相等的哈希码。

2. 如果允许更改键的hashCode会发生什么?

如上所述,哈希码有助于计算存储键值对的map中的桶位置。不同的哈希码值可能指的是不同的桶位置。

如果不小心,在将键值对放入map后,更改了键对象的哈希码,那么几乎不可能从map中获取值对象,因为我们不知道在哪个桶中放入了键值对。旧的键值对是不可访问的,因此这是一种内存泄漏情况。

在运行时,JVM为每个对象计算哈希码并按需提供。当我们修改对象的状态时,JVM设置一个标志,表示对象已被修改,并且必须再次计算哈希码。所以,下一次调用对象的hashCode()方法时,JVM会重新计算该对象的哈希码。

3. 我们应该使HashMap的键不可变

对于上述基本推理,建议键对象是不可变的。不可变性确保我们每次都会得到相同的哈希码,用于键对象。因此,它实际上一次性解决了几乎所有问题。但是,再次强调,这种类必须遵守hashCode()和equals()方法的合同。

这也是为什么像String、Integer或其他包装类这样的不可变类是良好的键对象候选项的主要原因。这也回答了为什么字符串是Java中流行的HashMap键的问题。

但请记住,不可变性是建议而不是强制性的。如果你想将可变对象用作HashMap中的键,那么你必须确保键对象的状态更改不会更改对象的哈希码。这可以通过重写hashCode()方法来实现。但是,你必须确保你遵守equals()的合同。

4. 自定义键的HashMap示例

在下面的示例中,我创建了一个Account类,仅包含两个字段以简化。我已经重写了hashCode和equals方法,以便它只使用帐户号来验证Account对象的唯一性。帐户类的所有其他可能属性都可以在运行时更改。

public class Account
{
    private int accountNumber;
    private String holderName;
    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }
    public String getHolderName() {
        return holderName;
    }
    public void setHolderName(String holderName) {
        this.holderName = holderName;
    }
    public int getAccountNumber() {
        return accountNumber;
    }
    //Depends only on account number
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + accountNumber;
        return result;
    }
    //仅比较 account numbers
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Account other = (Account) obj;
        if (accountNumber != other.accountNumber)
            return false;
        return true;
    }
}

这是否会导致不良行为?

不会。原因是Account类的实现遵守了这样的合同:“只要它们相等,相等的对象必须产生相同的哈希码,但不相等的对象不需要产生不同的哈希码。”

5. 示例

让我们测试一下上述分析中的Account类。

//创建具有可变键的HashMap
HashMap<Account, String> map = new HashMap<Account, String>();
//创建 key 1
Account a1 = new Account(1);
a1.setHolderName(\"A_ONE\");
//创建 key 2
Account a2 = new Account(2);
a2.setHolderName(\"A_TWO\");
//在map中存入可变键和值
map.put(a1, a1.getHolderName());
map.put(a2, a2.getHolderName());
//更改key状态,哈希码便重新计算
a1.setHolderName(\"Defaulter\");
a2.setHolderName(\"Bankrupt\");
//成功!我们能够获取value
System.out.println(map.get(a1)); //打印 A_ONE
System.out.println(map.get(a2)); //打印 A_TWO
//尝试使用新创建的具有相同account number的key
Account a3 = new Account(1);
a3.setHolderName(\"A_THREE\");
//成功!我们仍然能够取回账号1的value
System.out.println(map.get(a3)); //打印  A_ONE

6. 结论

在本教程中,我们学习了如何设计一个类,可以用作Map实例中存储键值对的键。

作为最佳实践:

  • 键类应该是不可变的。
  • 在大多数情况下,默认的hashCode()和equals()方法已经足够好,但如果重写了一个方法,那么应该重写另一个方法,以确保它们之间遵守合同。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/9386.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号