SUN官方的文档中规定"如果重定义equals方法,就必须重定义hashCode方法,以便用户可以将对象插入到散列(哈希)表中"
那么 SUN 公司是出于什么考虑做了这个规定呢?
在集合框架中的HashSet
,HashTable
和HashMap
都使用哈希表的形式存储数据,而hashCode
计算出来的哈希码便是它们的身份证。哈希码的存在便可以:
- 快速定位对象,提高哈希表集合的性能。
- 只有当哈希表中对象的索引即
hashCode
和对象的属性即equals
同时相等时,才能够判断两个对象相等。 - 从上面可以看出,哈希码主要是为哈希表服务的,其实如果不需要使用哈希表,也可以不重写
hashCode
。但是SUN公司应该是出于对程序扩展性的考虑(万一以后需要将对象放入哈希表集合中),才会规定重写equals
的同时需要重写hashCode
,以避免后续开发不必要的麻烦。 - hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
重写equals时需要注意满足java语言规范对于equals的要求:
Java语言规范要求equals
需要具有如下的特性:
- 自反性:对于任何非空引用 x,
x.equals()
应该返回true
。 - 对称性:对于任何引用 x 和 y,当且仅当
y.equals(x)
返回true
,x.equals(y)
也应该返回true
。 - 传递性:对于任何引用 x、y 和 z,如果
x.equals(y)
返回true
,y.equals(z)
也应返回同样的结果。 - 一致性:如果 x 和 y 引用的对象没有发生变化,反复调用
x.equals(y)
应该返回同样的结果。 - 对于任意非空引用 x,
x.equals(null)
应该返回false
。
重写equals时,用eclipes生成的源码:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
注意:1.equals
与hashCode
的定义必须一致,两个对象equals
为true
,就必须有相同的hashCode
。反之,如果两个对象的hashcode相同,equals不一定相同。2.当重写equals方法时,一定要同时把hashcode方法一并重写,因为要保证在实现hash表的扩展性