Java 中的 == 运算符、equals 方法和 hashCode 方法

简介: HashMap 中键是不可以重复的,因此它的键就必须是不同的对象,那么这个时候就先用计算速度快的 hashCode 进行比较,若哈希值都不相等,那么这两个对象必然不相等,若是相等的,那么这个就有两种可能出现,一种情况是这两个对象是真的相等,另外一种情况就是出现了罕见的“哈希冲突”现象,那么这个时候就轮到 equals 来进行判断了!在 HashMap 中,键是不可以重复的,也就是说,它们的键都是不相同的,因此就要判断不同对象是否为同一对象。因此,哈希值可以用于判断两个对象是否不等。

一、== 运算符

== 是 Java 中的一个运算符,用于比较两个对象,但在比较两个对象的时候需要根据比较类型分情况进行讨论。

1.1 基本数据类型与基本数据类型

基本数据类型之间通过 == 进行比较的时候,是直接比较它们的大小,而与它们的具体类型无关。

shortnum1=20000;
intnum2=20000;
System.out.println(num1==num2); // Output: true

image.gif

1.2 引用类型与引用类型

引用类型之间是比较它们的内存地址,因此两个同类型的对象通过 == 进行比较时的结果一般为 false。

publicclassTest {
publicstaticvoidmain(String[] args) {
Objo1=newObj();
Objo2=newObj();
System.out.println(o1==o2); // Output: false    }
}
classObj {}

image.gif

但也不是所有情况都会返回 false,基本数据类型的引用类型有个常量池的机制,使得它们就算是两个对象,依然会指向同一片内存空间,也就是说,它们依然是同一个对象。

Integernum1=100, num2=100;
System.out.println(num1==num2); // Output: true

image.gif

具体内容请见:八大基本类型及其包装类型

1.3 基本数据类型与引用类型

当基本数据类型与引用类型之间进行比较时,若引用类型是数字类的,那么就可以进行比较,其他类型的无法进行比较,会报错。

Integernum1=200;
intnum2=200;
System.out.println(num1==num2); // Output: trueintnum=200;
Stringstr="123";
System.out.println(str==num); // Error: 二元运算符 '==' 的操作数类型错误

image.gif

二、equals 方法

equals 是 Java 引用类型的一个基本方法,在没有对其进行重写(override)的情况下,equals 方法默认是比较两个对象的内存地址,重写之后视具体情况而定。

Stringstr1="1";
Stringstr2="2";
System.out.println(str1.equals(str2)); // Output: false

image.gif

2.1 equals 方法和 == 运算符的区别

    • == 是一个运算符,equals 是对象的方法(所以基本数据类型没有);
    • == 在比较基本数据类型时,是比较的值,比较对象的时候,是比较其内存地址;
    • equals 默认(没有被重写)是比较对象的内存地址,重写后是其他的内容(如 String 就是其对象所表示内容的值)

    三、hashCode 方法

    hashCode 是一个非常特殊的方法,它涉及到散列表的知识,哈希(hash)翻译过来就是散列的意思,它是一种算法,可以快速将任意对象转换为一个 16 进制的数值,而不同的对象之间,通过哈希算法得到的哈希值大概率是不会相同的,有极小概率会相同,此时就出现了一种被称为“哈希冲突”的情况,这里我们不对其做深究,具体内容请参见:哈希算法

    总之,在没有出现“哈希冲突”的情况下,我们就可以通过不同对象的哈希值不相同这一特性来判断两个对象是否为同一对象。

    Stringstr1="1";
    Stringstr2="2";
    System.out.println(str1.hashCode() ==str2.hashCode()); // Output: false

    image.gif

    当然,一般情况下我们是不会像上面这样用 hashCode 这一方法的,因为无法保证不会出现“哈希冲突”的情况。虽然不同对象的哈希值有可能相同,但是两个对象的哈希值的不同则说明它们一定不相同!因此,哈希值可以用于判断两个对象是否不等。

    四、区别和联系

    4.1 区别

    == 一般用来判断数字是否相等,equals 一般用来判断两个对象是否相等,而 hashCode 一般用来判断两个对象是否不等。

    == 只比较数字,判断速度最快,此外是 hashCode 方法,因为哈希值的计算十分快速,最后是 equals,默认情况下 equals 是比较两个对象的内存地址,这一过程虽然很快速,但更一般的情况是 equals 方法被重写了,比较的是两个非数字类型对象的属性值,因此它最慢。

    Stringstr1="1";
    Stringstr2="1";
    System.out.println(str1.equals(str2)); // Output: true

    image.gif

    上面的代码中,Java 内置引用类型 String 就重写了 equals 方法,它比较的是两个 String 对象的内容,而非内存地址。

    4.2 联系

    == 一般和 equals 和 hashCode 扯不上什么关系,从某种角度上来说,equals 和 hashCode 之间也没有必然联系。但是 Java 中存在一种数据结构,将 equals 和 hashCode 联系了起来,那就是 HashMap 及其子类。

    HashMap 中键是不可以重复的,因此它的键就必须是不同的对象,那么这个时候就先用计算速度快的 hashCode 进行比较,若哈希值都不相等,那么这两个对象必然不相等,若是相等的,那么这个就有两种可能出现,一种情况是这两个对象是真的相等,另外一种情况就是出现了罕见的“哈希冲突”现象,那么这个时候就轮到 equals 来进行判断了!这样一来,就高效且快速地解决了键不可重复的问题。

    从上面 HashMap 的比较中我们也可以看到,当我们自定义类型的时候,若要重写 equals 方法或者 hashCode 方法,请将它们两个同时进行重写,因为 Java 内置数据结构 HashMap 等判断键是否重复是需要同时调用它们两个。

    综上,我们可以总结为下表:

    比较方式 == 运算符 equals 方法 hashCode 方法
    比较速度 快速 默认情况下快速,一般情况下缓慢 中等
    比较值 数值 默认是内存地址,一般是对象属性 哈希值

    4.3 经典问题

    为什么重写了 hashCode 方法还要重写 equals 方法?如果只重写 equals 方法而不重写 hashCode 方法会有问题吗?在 HashMap 中怎样体现出来的?

    在 HashMap 中,键是不可以重复的,也就是说,它们的键都是不相同的,因此就要判断不同对象是否为同一对象。为了解决这个问题,HashMap 中同时调用了对象的 equals 方法和 hashCode 方法来进行判断。判断的部分源代码是这样的:

    if (e.hash==hash&&    ((k=e.key) ==key|| (key!=null&&key.equals(k))))
    returne;

    image.gif

    hashCode 方法和 equals 方法都可以用来比较两个对象,但是二者有一些区别:

    比较方式 equals 方法 hashCode 方法
    比较速度

    默认情况比较内存地址,速度快;

    重写后一般比较内容,速度慢

    计算哈希值,速度快
    比较值 内存地址或者内容(属性) 哈希值

    因此先用计算速度快的 hashCode 进行比较,这可以解决大部分问题,但由于可能出现“哈希冲突”,于是还需要 equals 方法解决。

    正因为在判断键是否重复这一问题需要同时调用 equals 方法和 hashCode 方法,故我们自己定义的类中重写它们时必须两个一起重写。若只重写 equals 方法,而不重写 hashCode 方法,那么创建两个内容一样但内存地址不同的对象并存储在 HashMap 中时,会被当成两个键,而不是一个键,进而引发其他错误。

    目录
    相关文章
    |
    20天前
    |
    Java
    Java中ReentrantLock中tryLock()方法加锁分析
    Java中ReentrantLock中tryLock()方法加锁分析
    13 0
    |
    9天前
    |
    Java 关系型数据库 MySQL
    Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
    【4月更文挑战第12天】Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
    46 3
    |
    21天前
    |
    Java
    Java 运算符
    4月更文挑战第8天
    |
    1天前
    |
    存储 Java 数据安全/隐私保护
    【Java探索之旅】运算符解密 位运算,移位运算
    【Java探索之旅】运算符解密 位运算,移位运算
    9 0
    |
    1天前
    |
    算法 Java
    【Java探索之旅】运算符解析 算术运算符,关系运算符
    【Java探索之旅】运算符解析 算术运算符,关系运算符
    8 0
    |
    1天前
    |
    IDE Java 开发工具
    基于Java程序设计的实验教学方法优化与实践
    基于Java程序设计的实验教学方法优化与实践
    9 1
    |
    3天前
    |
    存储 Java 索引
    【JAVA】HashMap的put()方法执行流程
    【JAVA】HashMap的put()方法执行流程
    |
    6天前
    |
    Java
    Java 与垃圾回收有关的方法
    Java 与垃圾回收有关的方法
    |
    6天前
    |
    Java
    Java基础&运算符
    Java基础&运算符
    |
    7天前
    |
    存储 Java 测试技术
    一文搞清楚Java中的方法、常量、变量、参数
    在JVM的运转中,承载的是数据,而数据的一种变现形式就是“量”,量分为:**常量与变量**,我们在数学和物理学中已经接触过变量的概念了,在Java中的变量就是在程序运行过程中可以改变其值的量。
    14 0