equals和hashCode

equals和hashCode


前言

在这里插入图片描述
在这里插入图片描述

equals 和 hashCode 两个方法属于 Object 基类的方法 从源码中我们可以看出 equals 方法默认比较的是两个对象的引用是否指向同一个内存地址。而 hashCode 是一个 native 本地方法(所谓的本地方法就是指不是用Java语言编写的,而是使用其他语言编写的程序,比如C/C++,一般是为了更快的与机器进行交互),其实默认的 hashCode 方法返回的就是对象对应的内存地址(注意是默认)。这一点我们通过 toString 方法也可以间接了解,我们都知道 toString 返回的是「类名@十六进制内存地址」,由源码可以看出内存地址与 hashCode() 返回值相同。

一、equals

equals 方法是基类 Object 的方法,所以我们创建的所有的对象都拥有这个方法,并有权利去重写这个方法。
在这里插入图片描述
String 类重写了 equals 方法,否则两个 String 对象内存地址肯定不同
String 类的 equals 方法:

public boolean equals(Object anObject) {
   //首先判断两个对象的内存地址(引用)是否相同
   if (this == anObject) {
       return true;
   }
   // 判断两个对象是否属于同一类型。
   if (anObject instanceof String) {
       String anotherString = (String)anObject;
       int n = value.length;
       //长度相同的情况下逐一比较 char 数组中的每个元素是否相同
       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;
}

equals 方法已经不单单是调用 this==obj来判断对象是否相同了。事实上所有 Java 现有的引用数据类型都重写了该方法。当我们自己定义引用数据类型的时候我们应该依照什么原则去判定两个对象是否相同,这就需要我们自己来根据业务需求来把握。但是我们都需要遵循以下规则:


自反性(reflexive)。对于任意不为 null 的引用值 x,x.equals(x) 一定是 true。

对称性(symmetric)。对于任意不为 null 的引用值 x 和 y ,当且仅当x.equals(y)true 时,y.equals(x)也是true。

传递性(transitive)。对于任意不为 null 的引用值x、y和z,如果 x.equals(y)true,同时 y.equals(z)true,那么x.equals(z)一定是 true。

一致性(consistent)。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时 x.equals(y) 要么一致地返回 true 要么一致地返回 false

二、equals方法和==

equals经常被拿来与==相区别。

我们都知道 Java 数据类型可分为 基本数据类型 和 引用数据类型。基本数据类型包括 byte, short, int , long , float , double , boolean ,char 八种。对于基本数据类型来说, == 就是比较的他们的值。

而对于引用类型来说, == 比较的就是它们所指向对象的内存地址。

equals 与 == 操作符的区别总结如下:

若 == 两侧都是基本数据类型,则判断的是左右两边操作数据的值是否相等

若 == 两侧都是引用数据类型,则判断的是左右两边操作数的内存地址是否相同。若此时返回 true , 则该操作符作用的一定是同一个对象。

Object 基类的 equals 默认比较两个对象的内存地址,在构建的对象没有重写 equals 方法的时候,与 == 操作符比较的结果相同。

equals 用于比较引用数据类型是否相等。在满足equals 判断规则的前体系,两个对象只要规定的属性相同我们就认为两个对象是相同的。

三、hashCode 方法

在 Java 中 hashCode 的存在主要是用于提高容器查找和存储的快捷性,如 HashSet, Hashtable,HashMap 等,hashCode是用来在散列存储结构中确定对象的存储地址的。
String 提供给我们的 hashCode 算法:

public int hashCode() {
    int h = hash;//默认是0
    if (h == 0 && value.length > 0) {
        char val[] = value;
         // 字符串转化的 char 数组中每一个元素都参与运算
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
 }

四、 hashCode 和 equals 方法的关系

Object 类对于 equals 方法的注释上有这么一条:

请注意,当这个方法被重写时,通常需要覆盖{@code hashCode}方法,以便维护{@code hashCode}方法的一般契约,
该方法声明相等对象必须具有相等的哈希码.

Object 对于 hashCode 方法也有几条要求:

在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象
进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需
保持一致。

如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须
生成相同的整数结果。

如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 
方法 不要求 一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希
表的性能。

总结

调用 equals 返回 true 的两个对象必须具有相等的哈希码。

如果两个对象的 hashCode 返回值相同,调用它们 equals 方法不一定返回 true 。

我们先来看下第一个结论:调用 equals 返回 true 的两个对象必须具有相等的哈希码。为什么这么要求呢?比如我们还拿 Set 集合举例,Set 首先会调用对象的 hashCode 方法寻找对象的存储位置,那么如果两个相同的对象调用 hashCode 方法得到的结果不同,那么造成的后果就是 Set 中存储了相同的元素,而这样的结果肯定是不对的。所以就要求 调用 equals 返回 true 的两个对象必须具有相等的哈希码。

那么第二条为什么 hashCode 返回值相同,两个对象却不一定相同呢?这是因为,目前没有完美的 hash 算法能够完全的避免 「哈希碰撞」,既然碰撞是无法完全避免的所以两个不相同的对象总有可能得到相同的哈希值。所以我们只能尽可能的保证不同的对象的 hashCode 不相同。事实上,对于 HashMap 在存储键值对的时候,就会发生这样的情况,在 JDK 1.7 之前,HashMap 对键的哈希值碰撞的处理方式,就是使用所谓的‘拉链法’。 具体实现会在之后分析 HashMap 的时候说到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别叹气了我走就是

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值