equals和hashCode两个方法是属于Object基类的两个方法,我们先来看看两个方法的默认实现。
equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到equals方法的默认实现是使用 == 比较两个对象,而对象使用 == 进行比较时,比较的是两个对象在堆内存中的内存地址。
hashCode方法:
public native int hashCode();
是native方法,不由Java实现,而是在运行时通过本地调用实现(默认是根据对象的内存地址生成),同时在文档中此方法有一个很重要的规范:If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result(如果两个对象通过equals方法比较后,结果是相等的,那么这两个对象分别调用hashCode方法必须得到相同的哈希码)。
还有一个很重要的描述:It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results(如果两个对象通过equals方法比较后,结果不相等,这两个对象分别调用hashCode方法时也有可能产生相同的哈希码)。
小结:
1、经过equals方法比较后,两个对象相等,那么两个对象的哈希码一定相同。
2、经过equals方法比较后,两个对象不相等,两个对象的哈希码不一定不相同。
User类:
package test;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User {
private int id;
private String name;
}
(1)重写equals,不重写hashCode:
重写equals方法:
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(!(obj instanceof User))
return false;
User user = ((User) obj);
return (this.getId() == user.getId()) && (this.getName().equals(user.getName()));
}
测试类:
package test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
public class UserTest {
public static void main(String[] args) {
User user1 = new User(3,"邱淑贞");
User user2 = new User(3,"邱淑贞");
System.out.println(user1 == user2);
System.out.println(user1.equals(user2));
HashSet set = new HashSet<>();
set.add(user1);
set.add(user2);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
User user = iterator.next();
System.out.println(user);
}
HashMap map = new HashMap<>();
map.put(user1,"test1");
map.put(user2,"test2");
System.out.println(map.get(user1));
System.out.println(map.get(user2));
}
}
测试结果:
1、==比较结果为false,因为比较的是对象的内存地址。
2、equals比较结果为true,因为重写了equals方法实现,两个对象的字段都相同,判定为同一个对象。
3、equals方法判定两个对象为同一对象,照理来说HashSet应该会进行去重操作,但迭代时仍然存在重复对象,这是因为HashSet是根据对象的哈希码进行去重操作,我们没有重写hashCode()方法,默认还是根据对象的内存地址生成哈希码。
4、map分别存储了两个相同的对象,照理说map在调用get方法获取value时,会以最后一次put操作为准(map不允许key重复,使用相同key重复调用put方法会将value覆盖),但这里两个相同对象(equals比较相等,对象的hashCode也理应相等)却分别映射了不同的value,产生了歧义,而原因与上述一致。
(2)既重写equals,也重写hashCode:
重写hashCode方法:
@Override
public int hashCode() {
return this.name.hashCode();
}
测试结果:
1、HashSet去重没有问题。
2、HashMap同一对象映射同一个value,重复put操作以最后一次为准。