所有的对象都继承自Object,equals方法是Object中的public方法。我们可以重写它。默认的equals方法是比较两个对象中的内存地址,而hashCode的值是根据对象的地址计算得到的(This is typically implemented by converting the internal address of the object into an integer)。如果你要override equals方法时,那你也必须要override hashCode方法。如果不重写的话,就会违反Object.hashcode的通用约定,违反的是下面第二条的约定,如果两个对象的equals方法比较相同,那么对象的hashCode方法也相同。违反这条约定会导致该类无法结合所有基于散列的集合一起正常工作,这样的集合包括HashMap、HashSet和Hashtable。
那你可能又会问了,hashCode怎么就影响了对象在Hash集合中的使用呢?
因为HashMap中有一项优化,可以将与每个项相关联的散列码缓存起来,如果散列码不匹配,也不必检验对象的等同性。
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
下面约定的内容是:
- 在应用程序执行期间,只要对象的equals方法的比较操作所用到的信息没有修改,那么对同一对象调用多次,hashCode方法必须始终如一的返回同一个整数。在同一个应用程序的多次执行过程中,每次执行返回的整数可以不一致。
- 如果两个对象根据equals方法比较是相同的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
- 如果两个对象根据equals方法比较是不相等的,那么调用这两个对象中任意一个hashCode方法,则不一定产生不同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高hashtable 的性能。
拓展学习:Hash表的存储结构。
理解了Hash表的存储结构,会对上面的内容有更深入的理解。
哈希表最常实现的方法是拉链法,可以理解为链表的数组,数组中的每个单元存放的是链表的首节点。
那这些元素按照什么样的规则存放在数组中呢?一般情况下通过hash(key)%len获得,也就是元素key的hash值对数组长度取模得到的。
这就是为什么第三个约定中说到,给不想等的对象产生截然不同的整数结果,有可能提高hashtable的性能。理想情况下,散列函数应该把集合中不相等的实例均匀地分布到所有的散列值中。
网友评论