equals()方法和hashCode()方法详解

1. Object类中equals()方法源代码如下所示:

/**
* Object类中的equals()方法
*/
public boolean equals(Object obj) {
return (this == obj);
}

由以上源代码知,Object类中的equals()方法是直接使用==运算符来判断两个对象相等的。

  • 引用类型变量使用==时,比较的是引用类型变量指向的对象的内存地址
  • 基本类型使用==时,比较值

Objcect类中的hashCode源代码如下:

 /**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java&trade; programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();// java8中的hashCode方法,

上面的注释中有说明如下几点:

  • 对象的hashCode值通常是根据对象的内存地址计算得来
  • 两个对象equals()结果为true时,两个对象的hashCode值一定相等,不同对象的hashCode不等
  • native标识此方法不是java语言实现

hashcode的用处:

hashCode()在哈希表中起作用,如HashSet、HashMap等

Object类中的toString()方法源代码如下:

public String toString() {
// 从这里就能看出打印对象时不重写toString()方法时,就会打印出对象的hashCode值
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

2. String类中equals()方法和hashCode()方法

String类中部分源代码如下所示:


/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/**
* 无参构造方法
*/
public String() {
this.value = "".value;
}
/**
* 有参构造方法
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
/**
*String类重写的equals方法
*/
public boolean equals(Object anObject) {
if (this == anObject) {// 此处的this指向a.equals(b)的a对象,即谁调用指向谁
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;
}
/**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using {@code int} arithmetic, where {@code s[i]} is the
* <i>i</i>th character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

从上面的源码中,我们不难发现String类已经重写了equals()方法和hashCode()方法。

String类重写的equals()方法判断流程如下:

  1. 使用==来判断两个对象的内存地址是否相同,相同返回true;
  2. 如果两个对象的内存地址不同,程序继续往下走,判断另一个对象是否是String类型的;
  3. 如果比较对象不是String类型,直接返回false
  4. 如果是String类型的,进行类型强转;
  5. 比较两个String的字符数组长度,如果长度不同,返回false
  6. 利用while循环来逐位比较字符是否相等,直到循环结束,所有字符都相等,则返回true,否则返回false;

下面来看一下重写的hashCode()方法。

  1. 首先String类中定义了一个int类型的变量hash用来缓存String对象的hash值;
  2. 如果当前调用hashCode()方法的String对象在常量池没有找到,并且该对象的length长度大于0,则继续往下走,否则返回0;即String类默认""字符串的hashCode()值为0;
  3. 遍历字符数组,获取每一个字符的ASCII码表对应的值 和之前的hash值相加,这样就保证了相同的字符串的hashCode()返回值相同,计算公式在注释里已经写出来了:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
  4. 将计算出来的结果保存到hash变量中,并返回该值;

这里为什么要乘以31呢?原因是为了性能,不仅仅指降低了计算速度,也降低了哈希冲突的概率。

哈希冲突:此处指不同的字符串生成了相同的hashCode值。

31是一个奇素数。如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算(低位补0)。使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果。 31 有个很好的性能,即用移位和减法来代替乘法,可以得到更好的性能: 31 * i == (i << 5)- i, 现代的 VM 可以自动完成这种优化。这个公式可以很简单的推导出来。 ---- 《Effective Java

素数:质数又称素数,指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。

3. 总结

一般会有如下的约定:

  1. equals()结果为true的两个对象,他们的hashCode()结果要相同
  2. hashCode()相等的两个对象,他们的equals()结果可以为false

也就是说,equals和hashcode方法是充分不必要的关系

所以,又有:

  1. 重写equals方法,必须重写hashcode方法
  2. 重写hashcode方法,可以不重写equals方法

一般建议都重写。

equals()方法和hashCode()方法详解的更多相关文章

  1. 详解equals()方法和hashCode()方法

    前言 Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,hashCode()方法用于计算对象的哈希码.equals()和hashCode()都不是final方 ...

  2. 详解 equals() 方法和 hashCode() 方法

    创建实体类时,最好重写超类(Object)的hashCode()和equals()方法 equals()方法: 通过该实现可以看出,Object类的实现采用了区分度最高的算法,即只要两个对象不是同一个 ...

  3. Java基础系列-equals方法和hashCode方法

    原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述         equals方法和hashCode方法都是有Object类定义的. publi ...

  4. Java 如何重写对象的 equals 方法和 hashCode 方法

    前言:Java 对象如果要比较是否相等,则需要重写 equals 方法,同时重写 hashCode 方法,而且 hashCode 方法里面使用质数 31.接下来看看各种为什么. 一.需求: 对比两个对 ...

  5. 关于Object类的equals方法和hashCode方法

    关于Object类的equals的特点,对于非空引用: 1.自反性:x.equals(x) return true : 2.对称性:x.equals(y)为true,那么y.equals(x)也为tr ...

  6. HashSet中存方用户自己定义数据类型数据,重写equals方法和hashCode方法

    import java.util.Set; import java.util.HashSet; public class SetTest { public static void main(Strin ...

  7. ThinkPHP 中M方法和D方法详解----转载

    转载的地址,http://blog.163.com/litianyichuanqi@126/blog/static/115979441201223043452383/ 自己学到这里的时候,不能清除的分 ...

  8. java中equals方法和hashcode方法的区别和联系,以及为什么要重写这两个方法,不重写会怎样

    一.在Object类中的定义为:public native int hashCode();是一个本地方法,返回的对象的地址值.但是,同样的思路,在String等封装类中对此方法进行了重写.方法调用得到 ...

  9. Hibernate中为什么要重写equals方法和hashcode方法

    1.*为什么要重写equals方法,首先我们来看一下equals源码: public boolean equals(Object anObject) { if (this == anObject) { ...

随机推荐

  1. 文档驱动 —— 表单组件(六):基于AntDV的Form表单的封装,目标还是不写代码

    开源代码 https://github.com/naturefwvue/nf-vue3-ant 也不知道大家是怎么写代码的,这里全当抛砖引玉 为何封装? AntDV非常强大,效果也非常漂亮,功能强大, ...

  2. Anaconda, conda, pyenv, virtualenv的区别

    1.Python环境 Python解释器--Python.exe Python包集合--Lib,包括自带包和第三方包 2.Anaconda--一个科学计算环境,Python的发行版本 包括了Conda ...

  3. Gitlab集成Sonarqube自动检测代码并发送报告给提交者

    使用gitlab-ci.sonarqube.sonar-scanner 实现如下功能 1.一旦提交代码就进行代码质量检测 2. 发送检测报告邮件给提交者 先来看下最终结果,邮件中有检测报告,具体bug ...

  4. Python-IndexError: list index out of range

    Error:IndexError: list index out of range Where? 对Python中有序序列进行按索引取值的时候,出现这个异常 Why? 对于有序序列: 字符串 str ...

  5. Centos-文本过滤-grep

    grep 文本过滤,对文本的每一行进行关键字搜索,如果找到则输出 相关选项 -A 除了列出符合关键字的行外,还输出符合关键字行后多少行内容 -c 只显示符号条件的行号 -f  批量搜索,把关键字写入到 ...

  6. 2020我终于成功搭建了Metasploitable3靶机

    0x00前言 在学习metasploit时我们往往需要一个靶场,下面为大家介绍一下如何在虚拟机中安装metasploitable 3靶场.Metasploitable3是Metasploitable2 ...

  7. 在Python中使用OpenCV(CV2)对图像进行边缘检测

    转载:https://blog.csdn.net/cumtb2002/article/details/107798767 Modules used: 使用的模块: For this, we will ...

  8. Java安全之Commons Collections1分析前置知识

    Java安全之Commons Collections1分析前置知识 0x00 前言 Commons Collections的利用链也被称为cc链,在学习反序列化漏洞必不可少的一个部分.Apache C ...

  9. ZooKeeper伪分布式集群安装及使用

    ZooKeeper伪分布式集群安装及使用 让Hadoop跑在云端系列文章,介绍了如何整合虚拟化和Hadoop,让Hadoop集群跑在VPS虚拟主机上,通过云向用户提供存储和计算的服务. 现在硬件越来越 ...

  10. 【源码项目】C语言编程之火车票管理系统!(最强代码)

    大学计算机软件技术基础课程设计任务书 一.题目: 火车票信息管理系统: 二.目的与要求 : ● 目的培养学生综合利用C++语言进行程序设计的能力, ● 培养学生的编程能力.用计算机解决实际问题的能力, ...