1。覆盖equals时总要覆盖hashCode
在覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违背Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运行,这样的集合包括HashMap,HashSet和HashTable。
以下是Object对象API关于equal方法和hashCode方法的说明:
1。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.
2。It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
以上API说明是对之前2点的官方详细说明
关于第一点,相等(相同)的对象必须具有相等的哈希码(或者散列码),为什么?
想象一下,假如两个Java对象A和B,A和B相等(eqauls结果为true),但A和B的哈希码不同,则A和B存入HashMap时的哈希码计算得到的HashMap内部数组位置索引可能不同,那么A和B很有可能允许同时存入HashMap,显然相等/相同的元素是不允许同时存入HashMap,HashMap不允许存放重复元素。
关于第二点,两个对象的hashCode相同,它们并不一定相同
也就是说,不同对象的hashCode可能相同;假如两个Java对象A和B,A和B不相等(eqauls结果为false),但A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同这时HashMap会在该位置建立一个链接表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用原则,是允许的。当然,哈希冲突越少越好,尽量采用好的哈希算法以避免哈希冲突。
Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么Hashset、Hashmap、Hashtable中的存储操作是根据什么原理来存取对象的呢?
下面以HashSet为例进行分析,我们都知道:在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?在java的集合中,判断两个对象是否相等的规则是:
1.判断两个对象的hashCode是否相等, 如果不相等,认为两个对象也不相等,完毕
如果相等,转入2(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。)
2.判断两个对象用equals运算是否相等,如果不相等,认为两个对象也不相等;如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
class Person {
String id;
String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return "id = " + id + " , name = " + name;
}
}
class Student {
String id;
String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
public int hashCode() {
return id.hashCode();
}
public boolean equals(Object ob) {
Student student = (Student)ob;
if(student.id.equals(this.id)) {
return true;
}else {
return false;
}
}
public String toString() {
return "id = " + id + " , name = " + name;
}
}
public class HashCodeTest {
public static void main(String[] args) {
testStringHash();
testHashSetWithoutEquals();
testHashSetWithEquals();
}
//测试没有实现equals和hashCode
public static void testHashSetWithoutEquals() {
Set personSet = new HashSet();
personSet.add(new Person("123", "Tom"));
personSet.add(new Person("123", "Tom"));
Iterator iterator = personSet.iterator();
while(iterator.hasNext()) {
Person person = iterator.next();
System.out.println(person.toString());
}
}
//测试实现了equals和hashCode
public static void testHashSetWithEquals() {
Set studentSet = new HashSet();
studentSet.add(new Student("123", "Tom"));
studentSet.add(new Student("123", "Tom"));
Iterator iterator = studentSet.iterator();
System.out.println("********************");
while(iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student.toString());
}
}
//String类覆盖了equals和hashCode参考jdk实现
public static void testStringHash() {
Object a = new Object();
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
System.out.println("hashCode s1 = " + s1.hashCode());
System.out.println("hashCode s2 = " + s2.hashCode());
Set hashSet = new HashSet();
hashSet.add(s1);
hashSet.add(s2);
Iterator it = hashSet.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
System.out.println("*******************");
}
}
同理hashMap的测试如下
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
class Person {
String id;
String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return "id = " + id + " , name = " + name;
}
}
class Student {
String id;
String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
public int hashCode() {
return id.hashCode();
}
public boolean equals(Object ob) {
Student student = (Student)ob;
if(student.id.equals(this.id)) {
return true;
}else {
return false;
}
}
public String toString() {
return "id = " + id + " , name = " + name;
}
}
public class HashMapTest {
public static void main(String[] args) {
testHashMapWithoutEquals();
testHashMapWithEquals();
}
public static void testHashMapWithoutEquals() {
Map hMap = new HashMap();
Person person1 = new Person("123", "Tom");
Person person2 = new Person("123", "Tom");
hMap.put(person1, "address");
hMap.put(person2, "address");
Iterator iterator = hMap.entrySet().iterator();
while(iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
Person key = (Person) entry.getKey();
String val = (String) entry.getValue();
System.out.println("key = " + key + " value = " + val);
}
}
public static void testHashMapWithEquals() {
System.out.println("*********************");
Map hMap = new HashMap();
Student student1 = new Student("123", "Tom");
Student student2 = new Student("123", "Tom");
hMap.put(student1, "address");
hMap.put(student2, "address");
Iterator iterator = hMap.entrySet().iterator();
while(iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
Student key = (Student) entry.getKey();
String val = (String) entry.getValue();
System.out.println("key = " + key + " value = " + val);
}
}
}
参考博客:Java提高篇——equals()与hashCode()方法详解