前言
在 Java 中,==
、equals
和 hashCode
是与对象比较和哈希计算相关的三个重要概念,它们既有区别又有联系。
==
==
的定义与作用
含义:==
是一个运算符,用于比较两个操作数的引用是否相同(对于基本数据类型则是比较值是否相等)。
适用范围:
- 基本数据类型(如
int
,float
,char
等):比较的是具体的值。 - 引用类型(如对象):比较的是两个引用是否指向内存中的同一个对象(即内存地址是否相同)。
特点:不关心对象的内容,只关心引用是否相同。
示例:
1 | String s1 = new String("hello"); |
equals
equals
的定义与作用
含义:equals
是 Object
类的一个方法,用于比较两个对象的内容是否相等。
默认行为:在 Object
类中,equals
的默认实现是使用 ==
,即比较引用是否相同。
自定义行为:可以通过重写 equals
方法来自定义比较逻辑,通常用于比较对象的内容(逻辑上的相等性)。
特点:
- 如果不重写,默认等价于
==
。 - 很多类(如
String
、Integer
等)已经重写了equals
,使其比较内容而非引用。
示例:
1 | String s1 = new String("hello"); |
hashCode
hashCode
的定义与作用
含义:hashCode
也是 Object
类的一个方法,返回对象的哈希码(一个整数),通常用于哈希表(如 HashMap
、HashSet
)中快速定位对象。
默认行为:Object
类中的默认实现基于对象的内存地址生成哈希码。
自定义行为:如果重写了 equals
,通常也需要重写 hashCode
,以满足“相等的对象必须具有相同的哈希码”这一约定。
特点:
- 哈希码不一定唯一,不同对象可能有相同的哈希码(哈希冲突)。
- 用于优化性能,尤其是在哈希集合中。
示例:
1 | String s1 = new String("hello"); |
对象的哈希码
提高数据结构的效率
哈希码的主要作用是为哈希表(如 HashMap
、HashSet
、Hashtable
等)提供一种快速定位对象的方式:
工作原理:哈希表通过哈希码将对象映射到特定的存储位置(称为“桶”)。当需要查找、插入或删除对象时,先用哈希码快速定位到对应的桶,然后在桶内进行进一步比较。
效率提升:相比逐一比较所有对象,哈希码将查找时间复杂度从 $O(n)$ 降低到接近 $O(1)$,尤其在数据量较大时效果显著。
示例:
1 | HashMap<String, Integer> map = new HashMap<>(); |
支持对象的相等性判断
哈希码与 equals
方法密切相关,共同用于判断对象是否相等:
契约:如果两个对象通过 equals
方法相等,它们的哈希码必须相同。这确保在哈希表中,相等的对象会被存储在同一个位置。
作用:哈希码作为第一步筛选,如果两个对象的哈希码不同,则无需调用 equals
,直接判定它们不相等,从而优化性能。
示例:
1 | String s1 = "hello"; |
区分不同的对象
哈希码可以作为对象的“指纹”,在一定程度上反映对象的独特性。
虽然哈希码并非绝对唯一(可能发生哈希冲突,即不同对象有相同哈希码),但它为区分对象提供了一种快速的初步手段。
注意:默认情况下(未重写 hashCode
),Object
类的哈希码基于对象的内存地址生成,因此不同对象通常有不同的哈希码。
支持哈希集合的唯一性
在 HashSet
等集合中,哈希码用于确保元素的唯一性:
当添加元素时,HashSet
会先检查哈希码是否已存在,如果存在,再用 equals
确认是否真正重复。
示例:
1 | HashSet<String> set = new HashSet<>(); |
自定义类的哈希行为
当你定义一个自定义类并希望它在哈希表中正确工作时,需要重写 hashCode
方法:
目的:确保逻辑上相等的对象(根据 equals
定义)具有相同的哈希码。
示例:
1 | class Person { |
实际应用中的注意事项
哈希冲突:不同对象可能有相同的哈希码,这种情况由哈希表通过链表或红黑树(Java 8+)处理。
性能要求:一个好的 hashCode
实现应尽量均匀分布哈希值,减少冲突,提高哈希表的效率。
不变性:对象的哈希码在对象生命周期中应保持不变。如果用于计算哈希码的字段(如 name
或 age
)被修改,可能导致对象在哈希表中“丢失”。
三者的联系
==
和 equals
==
关注引用相等,equals
(未重写时)也是如此。- 重写
equals
后,可以让它关注内容相等,与==
行为分离。
equals
和 hashCode
Java
有个重要约定(契约):
- 如果两个对象通过
equals
相等,则它们的hashCode
必须相等。 - 如果两个对象的
hashCode
不相等,则它们一定不equals
。 - 如果两个对象的
hashCode
相等,它们不一定equals
(因为可能有哈希冲突)。
因此,若重写了 equals
,必须同时重写 hashCode
,以保持一致性。
在集合中的作用
在 HashMap
或 HashSet
中,先用 hashCode
确定存储位置,再用 equals
判断对象是否真正相等。
注意事项
未重写的情况
1 | class Person { |
正确重写的情况
1 | class Person { |
后记
特性 | == |
equals |
hashCode |
---|---|---|---|
比较内容 | 引用(或基本类型值) | 默认引用,可重写为内容 | 返回哈希码 |
默认行为 | 内存地址比较 | 等同于 == |
基于内存地址 |
可重写 | 不可 | 可 | 可 |
使用场景 | 引用相等性 | 内容相等性 | 哈希表优化 |