Java - equals & hashcode

Java equals & hashcode

参考文章:

  • https://www.cnblogs.com/skywang12345/p/3324958.html
    equals 和 hashcode 两个方法属于Object基类的方法:
public boolean equals(Object obj) {
   return (this == obj);
}

public native int hashCode();

可以看出equals方法默认比较的是两个对象的引用是否指向同一个内存地址。而hashcode则是一个native方法。

hasCode 方法的注释这样说的: This is typically implemented by converting the internal address of the object into an integer,
根据官方文档中的hashcode描述,可以推断hashcode方法返回的就是对象对应的内存地址。

equals 详解

equals是Object中的方法,我们声明的类都默认继承自Object类,所以我们可以判断内存地址相等符不符合我们对类相等的定义。如果不符合,我们应该重写equals方法来实现类相等的标准。

for example:

String str1 = new String("abc");
String str2 = new String("abc");

str1 == str2; // false
str1.equals(str2); // true

可以看到String类一定重写了equals方法否则两个String对象的内存地址肯定不同。

 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方法时,Java文档给出了一些建议:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

  • 自反性(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。

对于任意不为 null 的引用值 x,x.equals(null) 返回 false。

延伸: equals vs ==
== 是比较两个对象的内存地址是否相同
equals 默认也是比较两个对象的内存地址是否相同
没有重写的equals方法和 == 操作符的结果是等价的。

当数据类型是基本数据类型时,用 == 即可判断对象是否相等。
当数据类型是引用数据类型时,需要看有没有重写equals,应用equals()判断对象是否相等。

Java 基本数据类型解析:

  • https://blog.csdn.net/a724888/article/details/80048774

hashCode

hash算法

hash算法,又称为散列算法。
hash算法简介:

hashCode常用于Map容器中,类似于 HashMap 这种使用了哈希算法容器会根据对象的hashCode返回值来初步确定对象在容器中的位置,然后内部再根据一定的 hash 算法来实现元素的存取。

hashCode 与hash算法的关系
hashCode返回由Hash算法生成的整数值。
相同的对象(根据equals())必须返回相同的哈希码。不同的对象不一定返回不同的哈希码。
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 方法与 java 中使用散列表的集合类息息相关,我们拿 Set 来举例,我们都知道 Set 中是不允许存放重复的元素的。那么我们凭借什么来判断已有的 Set 集合中是否有何要存入的元素重复的元素呢?有人可能会说我们可以通过 equals 来判断两个元素是否相同。那么问题又来,如果 Set 中已经有 10000个元素了,那么之后在存入一个元素岂不是要调用 10000 次 equals 方法。显然这不合理,性能低到令人发指。那要怎么办才能保证即高效又不重复呢?答案就在于 hashCode 这个函数。
经过之前的分析我们知道 hash 算法是使用特定的运算来得到数据的存储位置的,那么 hashCode 方法就充当了这个特定的函数运算。这里我们可以简单认为调用 hashCode 方法后得到数值就是元素的存储位置(其实集合内部还做了进一步的运算,以保证尽可能的均匀分布在桶内)。
当 Set 需要存放一个元素的时候,首先会调用 hashCode 方法去查看对应的地址上有没有存放元素,如果没有则表示 Set 中肯定没有相同的元素,直接存放在对应位置就好,但是如果 hashCode 的结果相同,即发生了碰撞,那么我们在进一步调用该位置元素的 equals 方法与要存放的元素进行比较,如果相同就不存了,如果不相同就需要进一步散列其它的地址。这样我们就可以尽可能高效的保证了无重复元素的方法。

作者:像一只狗
链接:https://juejin.im/post/6844903587764518920
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

面试题: hashCode 方法的作用和意义
答: 在 Java 中 hashCode 的存在主要是用于提高容器查找和存储的快捷性,如 HashSet, Hashtable,HashMap 等,hashCode是用来在散列存储结构中确定对象的存储地址的,

hashCode 和 equals的关系

Java对于equals方法:

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

可以看到如果我们出于某种原因复写了 equals 方法我们需要按照约定去覆写 hashCode 方法,并且使用 equals 比较相同的对象,必须拥有相等的哈希码。

对于hashCode方法:

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

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

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

结合 equals 方法的,我们可以做出如下总结:

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

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

我们先来看下第一个结论:调用 equals 返回 true 的两个对象必须具有相等的哈希码。为什么这么要求呢?比如我们还拿 Set 集合举例,Set 首先会调用对象的 hashCode 方法寻找对象的存储位置,如果两个相同的对象调用 hashCode 方法得到的结果不同,那么造成的后果就是 Set 中存储了相同的元素,而这样的结果肯定是不对的。所以就要求 调用 equals 返回 true 的两个对象必须具有相等的哈希码。
那么第二条为什么 hashCode 返回值相同,两个对象却不一定相同呢?这是因为,目前没有完美的 hash 算法能够完全的避免 「哈希碰撞」,既然碰撞是无法完全避免的所以两个不相同的对象总有可能得到相同的哈希值。所以我们只能尽可能的保证不同的对象的 hashCode 不相同。事实上,对于 HashMap 在存储键值对的时候,就会发生这样的情况,在 JDK 1.7 之前,HashMap 对键的哈希值碰撞的处理方式,就是使用所谓的‘拉链法’。 具体实现会在之后分析 HashMap 的时候说到。

本文总结了 equals 方法和 hashCode 方法的作用和意义。并学习了在覆写这两个方法的时候需要注意的要求。需要注意的是,关于这两个方法在面试的时候还是很有可能被问及的所以,我们至少要明白:

  1. hashCode 返回值不一定对象的存储地址,比如发生哈希碰撞的时候。
  2. 调用 equals 返回 true 的两个对象必须具有相等的哈希码。
  3. 如果两个对象的 hashCode 返回值相同,调用它们 equals 方法不一返回 true 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值