一. equals的使用
用来判断两个对象是否相等。
- 类如果没有重写Object的equals方法,则调用Object的equals方法,比较的是对象在堆中的地址是否相等
- 类如果重写了equals方法,一般用来比较对象的属性是否相等
二. 为什么要重写equals?
为什么要重写equals方法?主要看使用场景,如果不需要进行对象比较就不需要重写。
举个栗子,用户要修改用户名,传了个User对象过来,此时从DB里取出旧的User对象,比较两个是不是相等的(其实是比较用户名),如果没有重写equals方法,用的是Object的equals方法,比较对象的地址,与我们的意愿相违背。
例子1
public class Main {
public static void main(String[] args) {
User newUser = new User("dkangel");
User dbUser = new User("dkangel");
System.out.println(newUser.equals(dbUser));
}
}
1. 未重写equals方法,结果为false
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2. 重写equals方法,结果为true
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 重写equals方法,比较name属性是否相等
*
* @param obj 待比较对象
* @return boolean
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User user = (User) obj;
return Objects.equals(user.name, this.getName());
}
}
三. 重写了equals方法一定要重写hashCode方法吗?为什么?
hashCode方法的作用是获取哈希码(也称散列码),哈希码的作用是确定对象在哈希表中的位置。
先看下 hashCode 与 equals 的相关规定
- 如果两个对象相等,则 hashcode 一定也是相同的
- 两个对象相等,对两个对象分别调用 equals 方法都返回 true
- 两个对象有相同的 hashcode 值,它们也不一定是相等的
- hashCode方法的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode方法,则该类的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
从约定上看:重写equals方法必须重写hashCode方法。
从使用上看:
-
如果类在使用过程中不需要创建类对应的哈希表(就是不会在HashMap/HashSet/HashTable等用到hash的数据数据里用到该类),那就与hashCode方法没什么关系,也就不需要重写hashCode方法。如上面的例子1,只重写了equals方法,没有重写hashCode方法,并不影响使用。
-
如果类在使用过程中需要创建类对应的哈希表,那就需要重写hashCode方法,为什么?再举个栗子
例子2
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与该位置其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。
public class Main {
public static void main(String[] args) {
User newUser = new User("dkangel");
User dbUser = new User("dkangel");
User otherUser = new User("zhangsan");
HashSet<User> users = new HashSet();
users.add(newUser);
users.add(dbUser);
users.add(otherUser);
System.out.println("users size: " + users.size());
users.forEach(user -> System.out.printf("name: %s, hashCode: %s\n", user.getName(), user.hashCode()));
}
}
1. 未重写hashCode方法
结果为3
,两个name为“dkangel”的对象由于未重写hashCode方法,导致set中出现重复值。
2. 重写hashCode方法
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User user = (User) obj;
return Objects.equals(user.name, this.getName());
}
@Override
public int hashCode() {
return this.name.hashCode();
}
}
结果为2
,重写了hashCode方法两个name为“dkangel”的对象只添加了一个到set里。