关于==与equals()的区别和hashCode()和equals()的重写的理解
Kang Lv3

1、Java 数据类型

首先要介绍一下 Java 的数据类型,Java 提供两种不同的数据类型:基本数据类型和引用数据类型

基本数据类型

基本数据类型共八种,分为三大类。除了这八种,其余的都是引用数据类型。
基本数据类型

  1. 基本数据类型的值是一个简单的数字,字符或布尔值,Java 虚拟机会为其分配数据类型实际占用的内存空间;
  2. 基本数据类型的变量是局部变量时,存储在栈里面;是成员变量时,存放在堆里;
  3. 基本数据类型的值是不可变的(不能给基本数据类型添加属性和方法);
  4. 基本数据类型的比较是值的比较(他们的值相等时就是相等的)。

引用数据类型

引用数据类型可以分为:类、接口、数组。
引用数据类型

  1. 引用数据类型的变量的值是一个地址,而这个地址指向的堆内存中的一个值或一组值;
  2. 引用数据类型的值是可变的,且可以拥有属性和方法

其中字符串 String 属于引用数据类型。

1
2
3
4
5
String a = "China";
String b = a; // a、b都指向常量池中的China

String c = new String("China");
String d = c; // c、d存储同一个地址,这个地址指向栈中的China

2、== 和 equals() 的区别

使用 == 比较

对于基本数据类型:比较值是否相等
对于引用数据类型:比较地址是否相同

使用 equals() 比较

equals() 方法是 Object 类的一个方法,Java当中所有的类都是继承于 Object 这个超类。如果没有对该方法进行重写的话,调用的仍然是 Object 类中的方法,而 Object 中的 equals() 方法返回的是 == 的判断。
Object 的 equals() 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals() 。

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

在实际使用中,一般会重写 equals() 方法,如 String 类的 equals() 源码如下。
即两个字符串使用 == 相等 或者 比较的字符串是否属于 String 且 两个字符串 hashcode 相等 且 两个字符串所有组成字符都相等返回 true,其他情况返回 false。

1
2
3
4
5
6
7
8
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}

基本数据类型不能使用 equals()

== 和 equals() 的区别举例

  • String str = “China”; 先在内存中找是不是有”China”这个对象,如果有,就让str指向那个”China”.如果内存里没有”China”,就创建一个新的对象保存”China”;
  • String str=new String(“China”) 是不管内存里是不是已经有”China”这个对象,都新建一个对象保存”China”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class test1 {
public static void main(String[] args) {
String a = new String("China"); // a 为一个引用
String b = new String("China"); // b为另一个引用,对象的内容一样
String aa = "China"; // 放在常量池中
String bb = "China"; // 从常量池中查找 China
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true,重写
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}

当使用自动装箱方式创建一个Integer对象时,当数值在-128 ~ 127时,会将创建的 Integer 对象缓存起来,当下次再出现该数值时,直接从缓存中取出对应的Integer对象;数值在范围外时,都会创建新的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class test1 {
public static void main(String[] args) {
Integer x1 = 3;
Integer y1 = 3;
Integer x2 = 300;
Integer y2 = 300;
System.out.println(x1 == y1);// true
System.out.println(x2 == y2);// false

Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false
System.out.println(a.equals(b));//true
}
}

3、重写 equals() 时,为什么也要重写 hashCode()

hashCode() 的作用

hashCode() 的作用是获取哈希码,返回一个int整数,这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode() 定义在 Object 中,即每个对象都包含 hashCode() 函数。但是 hashCode() 只有在散列表(HashMap,HashTable,HashSet)中才有用,在其他情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!
散列表的本质是通过数组实现的。当我们要获取散列表中的某个“值”时,实际上是要获取数组中的某个位置的元素。而数组的位置,就是通过“键”来获取的;更进一步说,数组的位置,是通过“键”对应的散列码计算得到的。

所以,如果两个对象相等,它们的 hashCode() 值一定相等;如果他们的 hashCode() 相等,它们不一定相等。

hashCode() 和 equals() 的关系

存入数据时,先比较 hashCode() 的值是否相等,如果相等的话,再使用 equals() 进行比较,如果值也相等,则说明他们相等。如果 equals() 不相等,说明这是两个不同的对象。

为啥要重写

因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

from 知乎
在 hashmap 中是要保证 key 的唯一,如果新的 key 放入 map 中,发现和已有的 key 相同,那么就要覆盖。那么这个“唯一”是怎么确定的?或者说怎么认为两个 key 是相同的?那么这里的相同是指内容相同,比如 new String(“aaa”) ,new String(“aaa”), new 了两个字符串,是两个对象,但是内容是相同的,我们认为他们是相同的 key,再比如两个 User 对象,他们的 name 和 age 属性都相同,则认为他们是相同的。如果把 hashCode 去除,去除后,通过 equals 来比较两个 key 是否相同,也能达到我们的要求,只不过就要和 map 中的 key 一个一个的用 equals 比较,如果 map 中有很多元素了,那么效率可能会很低。其实 hashmap 中使用 hashCode 的一个目的是“分组”,同一个链表上的 key 的 hashcode 是一样的,如果不重写 hashcode,那么两个相同内容的对象,就会放在不同的链表上,那么就会存在两个相同的 key,不符合我们的初衷。所以就要重写 hashcode,保证有相同内容的对象有相同的 hashcode。而且 hashcode 的结果跟对象的属性有关。如果属性不参与 hashcode 的计算,那么这个 hash 算法就无意义。

  • 本文标题:关于==与equals()的区别和hashCode()和equals()的重写的理解
  • 本文作者:Kang
  • 创建时间:2022-01-09 21:02:38
  • 本文链接:ykhou.github.io2022/01/09/关于-与equals-的区别和hashcode-和equals-的重写的理解/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!