Java hashCode()方法

本文深入解析hashCode()与equals()方法的关系及其在HashMap等散列数据结构中的应用,阐述了它们如何影响对象比较与数据存储效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前边
  • 现在的时间:2019-06-05
  • 一共讨论两个问题:hashCode()方法与euqals()方法的关系、hashCode()方法在HashMap等散列的数据结构中有什么作用
  • 没有高深的理论,以简单理解为主.

hashCode()方法与euqals()方法的关系
euqals()方法讲解
  • 第一次学Java的时候,有一个重点你肯定知道:比较两个字符串的内容是否相等要用euqals()方法,不能用==.
  • 所以我们可以这样理解:euqals()方法是用来比较两个对象中的内容是否相同的,相同会返回true,不相同返回flase.
  • 那对象中的内容指的是什么呢,其实就是对象中的属性,对String来说就是字符串的内容了.
  • 但如果我们自己定义一个类然后创建两对象,用equals()方法比较这两个属性值一样的对象,结果却会返回false.
  • 为什么会这样呢,当对象p1调用euqals()方法时,因为它所属的类中并没有重写这个方法,所以会去调用父类中的euqals()方法,也就是Object中的equals()方法,其源码如下
    public boolean equals(Object obj)
    {
            return (this == obj);
    }
    
    从源码中可以看出,只有两个对象是同一个对象时,才会返回true,但我们要求只要两个对象的所有属性值相同即返回true.
  • 为了达到只要两个对象的所有属性值相同即返回true的要求,我们要在People类中重写Object中的equals()方法.
  • 那么,为什么String的对象(字符串)调用equals()方法就是比较两个字符串的内容是否相同而不是Object中equals()方法写的比较是否是同一个对象呢,这是因为String类已经重写了equals()方法,其源码如下:
    public boolean equals(Object anObject) 
    {
        if (this == anObject) 
        {
            return true;
        }
        if (anObject instanceof String) 
        {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) 
            {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) 
                {
                    if (v1[i] != v2[i])
                    {
                        return false;
                    }
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    
hashCode()方法与euqals()方法的关系
  • hashCode()方法会返回一个int值,这个int值就是这个对象对应的哈希值.
  • 这个方法也存在于Object中,其源代码是public native int hashCode();
  • 就现在所讲的而言,可以这样理解hashCode()的作用:这个方法会返回调用它的对象的哈希值,这样就把哈希值与这个对象对应起来了,一个哈希值可能对应着多个对象,但一个对象只对应一个哈希值.
  • hashCode()euqals()有以下关系:
    • 如果equals()为true,说明是同一个对象,那么两个对象的hashCode()一定相同.
    • 如果equals()为false,说明不是同一个对象,那么两个对象的hashCode()可能相同也可能不同.
    • 如果两个对象hashCode()相同,则一定是同一个对象,equals()为true.
    • 如果两个对象hashCode()不同,则可能是同一个对象也可能不是同一个对象,equals()可能为true,也可能为false.
  • 可以看到当equals()方法为true时,两个对对象调用hashCode()的值一定相同,所以我们有以下结论:如一个类重写了equals()方法,那么它必须也重写hashCode()方法,否则就会出现equals()为true但hashCode()不同的情况.

hashCode()方法在HashMap等散列的数据结构中的作用
HashMap简单回顾
  • HashMap中的数据是以K-V的形式存在的.
  • 数据是无序不可重复的,不可重复指的是K-VK不可重复.
  • 如果存入有相同KK-V数据,那么这条数据的V会覆盖原来数据的V.
如何保证不可重复

下边是最通俗的方法,但在Java中并不使用这种方法,这只是为了引出hashCode()的使用而意想出来的.

  • 假设现在HashMap中已经有了100条K-V数据.
  • 现在要存入一条新的K-V,那么就拿要存入数据的K和每一条数据的K比较,如果遇到相同的就覆盖数据,假如全都不相同,那么存入这条K-V.
    • 也就是说用equals()方法比较新的K与已存在的K,相同则V覆盖上去,全不相同则K-V存入.
  • 可见如果要存入这条数据最坏的情况要比较100次,最好的情况要比较1次.
    • K-V只存入了V即要存入的数据覆盖了原来的数据,我们称为存入了数据
    • K-V原先K就不存在KV都存入了,我们也称为存入了数据
使用hashCode()保证不可重复

这是Java使用的方法

  • 还是假设HashMap中已经有了100条K-V数据.
  • 假设这100个K是:K1,K2,K3,...,K100
  • HashMap会用每个K调用hashCode()方法,计算出每个K的哈希值,前边我们说过一个哈希值可能对应着多个对象,但一个对象只对应一个哈希值,所以最后可以形成这样一个表.
哈希值哈希值对应的K
9527K1,K34,K22,…
7855K3,K5,K99,…
7865K9,K77,K49,…
  • 当我们再要存入一条新的K-V时,HashMap会调用hashCode()计算出新的K-V的K对应的哈希值,然后与表中的哈希值比较.
    • 如果有相同的哈希值,那么需要再调用equals()方法比较这个新的K这个哈希值对应的所有K,因为hashCode()相同,equals()不一定为false也不一定为true,如果结果为true说明原先有个K和新的K相同,新的V覆盖原来的V;如果结果全部为false,则将K-V存入.
    • 如果没有相同的哈希值,则直接存入新的K-V,因为hashCode()不同则equals()一定为false,则新的K一定不存在.
  • 可见,要存入数据最好的情况需要比较2次,最坏的情况需要比较f(a,b)maxf(a,b)_{max}f(a,b)max
    • f(a,b)=a+ba∈[1,那个表格的行数]b∈[1,K值最多的那一行K的个数]f(a,b)=a+b \\ a\in[1,那个表格的行数] \\b\in[1,K值最多的那一行K的个数]f(a,b)=a+ba[1,]b[1,KK]
    • 最坏的情况需要比较的次数虽然没办法准确确定,但显示小于原先的100次.
  • 如此,使用hashCode(),在存入新的数据时便大大减少了用equals()比较的次数,提高了效率,这便是hashCode()在HashMap等散列的数据结构中的作用.
### Java 中 `hashCode` 方法的使用与实现 #### 1. 定义和作用 在 Java 编程中,`hashCode()` 是一个由 Object 类定义的方法,它的主要功能是返回对象的一个整数值作为散列码。这个方法的作用在于支持基于哈希表的数据结构(如 HashMap 和 HashSet),以便快速查找数据[^1]。 当对象被存储到这些集合类中时,`hashCode()` 的值会被用来决定对象存储的位置。如果两个对象相等,则它们的 `hashCode()` 值也应当相同;反之亦然。因此,在设计自定义类时,通常需要重写 `equals()` 和 `hashCode()` 方法来满足这一约束条件[^2]。 #### 2. 实现原则 为了确保程序能够高效运行并减少冲突的可能性,开发者应遵循以下几点准则: - **一致性**:只要对象的内容未发生变化,那么多次调用同一个实例上的 `hashCode()` 应始终返回相同的值。 - **分布均匀性**:不同对象产生的 hash code 尽量分布在尽可能多的不同桶里,从而降低碰撞概率。 - **简单易懂**:虽然追求最佳性能很重要,但是也要考虑代码可读性和维护成本之间的平衡[^3]。 #### 3. 示例代码分析 下面给出了一种常见的做法来生成有效的 hash codes: ```java @Override public int hashCode(){ final int primeNumber = 31; int resultValue = 1; resultValue = primeNumber * resultValue + ((this.x == null)?0:this.x.hashCode()); resultValue = primeNumber * resultValue + (int)(this.y ^ (this.y >>> 32)); if(this.z !=null){ char[] charsInZ=this.z.toCharArray(); for(char c:charsInZ){ resultValue=primeNumber*resultValue+c; } } return resultValue; } ``` 上述例子展示了如何通过组合多个字段创建复合 key 的 hash function 。这里采用了乘积加法模式,并选择了质数因子(这里是31),因为这样能更好地混合位操作结果,进而改善最终输出的质量[^4]。 #### 注意事项 尽管提供了默认版本,但在某些情况下仍需手动覆盖此函数。特别是当我们希望依据特定逻辑判断两事物是否相当时更是如此。另外需要注意的是,即使实现了良好的 hash functions ,也不能完全消除 collisions (即不同的 keys 映射到了同一 bucket 上)。不过合理的设计确实可以帮助我们将其影响降到最低限度。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值