思维导图:
==和equals都是Java中判断两个变量是否相等的方式。他们具有如下区别:
==
是 运算符,可用于比较基本数据类型和引用数据类型。equals()
是 Object
类的方法,只能用于比较引用数据类型(对象),基本数据类型无此方法。
2.1对于基本数据类型来说
==
直接比较值是否相等,equals()
无法用于基本数据类型,因为它是对象方法,基本类型没有此方法。
2.2对于引用数据类型来说:
==
比较 对象的内存地址 是否相同(即是否指向同一个对象)。equals()
:若未重写,默认与 ==
行为一致(比较内存地址)。若重写(如 String
、Integer
等类)equals(),则按重写逻辑比较对象内容。
代码示例:
public boolean equals(Object obj) {
return (this == obj); //默认比较内存地址
}
public class EqualsMethodExample {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1.equals(str2)); // 输出: true,因为 String 类重写了 equals() 方法,比较的是字符串内容
Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(obj1.equals(obj2)); // 输出: false,因为使用的是 Object 类的默认 equals() 方法,比较的是内存地址
}
}
==
。==
。equals()
),用 equals()
。例如判断两个 String
内容是否相同、两个自定义对象的属性是否一致等。特性 | == 运算符 |
equals() 方法 |
---|---|---|
所属类型 | 运算符 | Object 类的方法 |
基本类型比较 | 比较值是否相等 | 不可用(仅用于对象) |
引用类型比较 | 比较内存地址是否相同 | 未重写时比地址,重写后按逻辑比内容 |
能否重写 | 不能(运算符无法重载) | 可以(根据业务需求自定义比较逻辑) |
equals()
方法的步骤
- 检查引用是否相同:使用
==
运算符检查两个引用是否指向同一个对象,如果是则直接返回true
。- 检查对象是否为
null
:检查传入的对象是否为null
,如果是则返回false
。- 检查对象类型是否相同:使用
instanceof
运算符检查传入的对象是否为当前类或其子类的实例,如果不是则返回false
。- 强制类型转换:将传入的对象强制转换为当前类的类型。
- 比较对象的属性:比较当前对象和传入对象的关键属性是否相等。
代码示例:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 重写 equals() 方法
@Override
public boolean equals(Object obj) {
// 检查引用是否相同
if (this == obj) {
return true;
}
// 检查对象是否为 null
if (obj == null) {
return false;
}
// 检查对象类型是否相同
if (!(obj instanceof Person)) {
return false;
}
// 强制类型转换
Person other = (Person) obj;
// 比较对象的属性
return this.name.equals(other.name) && this.age == other.age;
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
Person person3 = new Person("Bob", 30);
System.out.println(person1.equals(person2)); // 输出: true
System.out.println(person1.equals(person3)); // 输出: false
}
}
但需要注意的是,在重写equals()时,一般也要重写hashcode(),那这是为什么呢?
一、Java 对象相等判断的约定
根据 Java规范,equals() 和 hashCode() 需满足以下关系:
1.若两个对象 equals() 返回 true,它们的 hashCode() 必须相同。
2.若两个对象 hashCode() 不同,equals() 必须返回 false。
这是为了确保对象在哈希表(如 HashSet、HashMap)中行为的一致性。若只重写 equals() 而不重写 hashCode(),可能出现两个 equals() 相等的对象 hashCode() 不同,导致哈希表无法正确识别重复元素或键,破坏数据结构的逻辑。
二、哈希表的底层原理
哈希表(如 HashMap、HashSet)通过 “先比较 hashCode,再比较 equals” 来提升效率:
1.定位存储位置:先通过 hashCode() 计算哈希值,确定对象在哈希表中的桶(数组下标)。若 hashCode 不同,直接判定对象不等,无需调用 equals(),减少比较次数。
精确判断相等:若 hashCode 相同,再通过 equals() 确认对象内容是否真正相等。
若只重写 equals(),未重写 hashCode(),则默认 hashCode() 基于对象内存地址计算。此时两个 equals() 相等的对象(如自定义类的两个实例,内容相同但内存地址不同),hashCode() 可能不同,导致:在 HashSet 中被视为不同元素,无法正确去重。在 HashMap 中被存储到不同桶位,无法通过键正确获取值,破坏数据一致性。
代码示例:
假设自定义类 Person只重写 equals()
,未重写 hashCode()
:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return name.equals(person.name) && age == person.age;
}
}
当将两个内容相等的 Person
对象存入 HashSet
时:
HashSet set = new HashSet<>();
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
set.add(p1);
set.add(p2);
System.out.println(set.size()); // 输出 2,本应去重但未实现
由于未重写 hashCode()
,p1
和 p2
的 hashCode
不同(基于内存地址),HashSet
认为它们是不同元素。若同时重写 hashCode()
:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return name.equals(person.name) && age == person.age;
}
@Override
public int hashCode() {
return Objects.hash(name, age); // 根据内容计算 hashCode
}
}
解释说明:此时 p1
和 p2
的 hashCode
相同,HashSet
会通过 equals()
确认相等,正确去重,set.size()
输出 1
。
总结:
因此,在涉及对象比较或使用哈希表相关集合时,两者必须同时重写,且基于相同的属性逻辑,以保证程序的正确性和性能。