抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

在 Java 中,==equalshashCode 是与对象比较和哈希计算相关的三个重要概念,它们既有区别又有联系。

==

== 的定义与作用

含义== 是一个运算符,用于比较两个操作数的引用是否相同(对于基本数据类型则是比较值是否相等)。

适用范围

  • 基本数据类型(如 int, float, char 等):比较的是具体的值。
  • 引用类型(如对象):比较的是两个引用是否指向内存中的同一个对象(即内存地址是否相同)。

特点:不关心对象的内容,只关心引用是否相同

示例

1
2
3
4
5
6
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false,因为 s1 和 s2 是不同的对象
String s3 = "hello";
String s4 = "hello";
System.out.println(s3 == s4); // true,因为字符串常量池优化,指向同一个对象

equals

equals 的定义与作用

含义equalsObject 类的一个方法,用于比较两个对象的内容是否相等。

默认行为:在 Object 类中,equals 的默认实现是使用 ==,即比较引用是否相同。
自定义行为:可以通过重写 equals 方法来自定义比较逻辑,通常用于比较对象的内容(逻辑上的相等性)。

特点

  • 如果不重写,默认等价于 ==
  • 很多类(如 StringInteger 等)已经重写了 equals,使其比较内容而非引用。

示例

1
2
3
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true,因为 String 重写了 equals,比较内容

hashCode

hashCode 的定义与作用

含义hashCode 也是 Object 类的一个方法,返回对象的哈希码(一个整数),通常用于哈希表(如 HashMapHashSet)中快速定位对象。

默认行为:Object 类中的默认实现基于对象的内存地址生成哈希码。
自定义行为:如果重写了 equals,通常也需要重写 hashCode,以满足“相等的对象必须具有相同的哈希码”这一约定。

特点

  • 哈希码不一定唯一,不同对象可能有相同的哈希码(哈希冲突)。
  • 用于优化性能,尤其是在哈希集合中。

示例

1
2
3
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.hashCode() == s2.hashCode()); // true,因为 String 重写了 hashCode

对象的哈希码

提高数据结构的效率

哈希码的主要作用是为哈希表(如 HashMapHashSetHashtable 等)提供一种快速定位对象的方式:

工作原理:哈希表通过哈希码将对象映射到特定的存储位置(称为“桶”)。当需要查找、插入或删除对象时,先用哈希码快速定位到对应的桶,然后在桶内进行进一步比较。

效率提升:相比逐一比较所有对象,哈希码将查找时间复杂度从 $O(n)$ 降低到接近 $O(1)$,尤其在数据量较大时效果显著。

示例

1
2
3
4
HashMap<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
// 查找 "Alice" 时,先用 "Alice".hashCode() 定位桶,再用 equals 确认
System.out.println(map.get("Alice")); // 25

支持对象的相等性判断

哈希码与 equals 方法密切相关,共同用于判断对象是否相等:

契约:如果两个对象通过 equals 方法相等,它们的哈希码必须相同。这确保在哈希表中,相等的对象会被存储在同一个位置。

作用:哈希码作为第一步筛选,如果两个对象的哈希码不同,则无需调用 equals,直接判定它们不相等,从而优化性能。

示例

1
2
3
4
String s1 = "hello";
String s2 = "hello";
System.out.println(s1.hashCode() == s2.hashCode()); // true
System.out.println(s1.equals(s2)); // true

区分不同的对象

哈希码可以作为对象的“指纹”,在一定程度上反映对象的独特性。

虽然哈希码并非绝对唯一(可能发生哈希冲突,即不同对象有相同哈希码),但它为区分对象提供了一种快速的初步手段。

注意:默认情况下(未重写 hashCode),Object 类的哈希码基于对象的内存地址生成,因此不同对象通常有不同的哈希码。

支持哈希集合的唯一性

HashSet 等集合中,哈希码用于确保元素的唯一性:

当添加元素时,HashSet 会先检查哈希码是否已存在,如果存在,再用 equals 确认是否真正重复。

示例

1
2
3
4
HashSet<String> set = new HashSet<>();
set.add("Alice");
set.add("Alice"); // 不会重复添加,因为 hashCode 和 equals 都相同
System.out.println(set.size()); // 1

自定义类的哈希行为

当你定义一个自定义类并希望它在哈希表中正确工作时,需要重写 hashCode 方法:

目的:确保逻辑上相等的对象(根据 equals 定义)具有相同的哈希码。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
String name;
int age;
Person(String name, int age) { this.name = name; this.age = age; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Person)) return false;
Person other = (Person) obj;
return name.equals(other.name) && age == other.age;
}
@Override
public int hashCode() {
return 31 * name.hashCode() + age; // 简单哈希算法
}
}

实际应用中的注意事项

哈希冲突:不同对象可能有相同的哈希码,这种情况由哈希表通过链表或红黑树(Java 8+)处理。

性能要求:一个好的 hashCode 实现应尽量均匀分布哈希值,减少冲突,提高哈希表的效率。

不变性:对象的哈希码在对象生命周期中应保持不变。如果用于计算哈希码的字段(如 nameage)被修改,可能导致对象在哈希表中“丢失”。

三者的联系

==equals

  • == 关注引用相等,equals(未重写时)也是如此。
  • 重写 equals 后,可以让它关注内容相等,与 == 行为分离。

equalshashCode

Java 有个重要约定(契约):

  1. 如果两个对象通过 equals 相等,则它们的 hashCode 必须相等。
  2. 如果两个对象的 hashCode 不相等,则它们一定不 equals
  3. 如果两个对象的 hashCode 相等,它们不一定 equals(因为可能有哈希冲突)。

因此,若重写了 equals,必须同时重写 hashCode,以保持一致性。

在集合中的作用

HashMapHashSet 中,先用 hashCode 确定存储位置,再用 equals 判断对象是否真正相等。

注意事项

未重写的情况

1
2
3
4
5
6
7
8
9
class Person {
String name;
Person(String name) { this.name = name; }
}
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1 == p2); // false
System.out.println(p1.equals(p2)); // false,未重写 equals
System.out.println(p1.hashCode() == p2.hashCode()); // false,未重写 hashCode

正确重写的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
String name;
Person(String name) { this.name = name; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Person)) return false;
Person other = (Person) obj;
return name.equals(other.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode() == p2.hashCode()); // true

后记

特性 == equals hashCode
比较内容 引用(或基本类型值) 默认引用,可重写为内容 返回哈希码
默认行为 内存地址比较 等同于 == 基于内存地址
可重写 不可
使用场景 引用相等性 内容相等性 哈希表优化

评论