hashCode和equals方法是Object类中的两个常用方法。其定义如下:
// hashCode()方法默认是native方法:
public native int hashCode();
// equals(obj)默认比较的是内存地址:
public boolean equals(Object obj) {
return (this == obj);
}
hashCode()方法有三个关注点:
在这种缺省实施情况下,只有它们引用真正同一个对象时这两个引用才是相等的。同样,Object提供的hashCode()的缺省实施通过将对象的内存地址对映于一个整数值来生成。
HashMap内部是由Entry<K,V>类型的数组table来存储数据,其初始状态如下:
static final Entry<?,?>[] EMPTY_TABLE = {};
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
static class Entry<K,V> implements Map.Entry<K,V>{
final K key;
V value;
Entry<K,V> next;
// key的hashCode方法的返回值经过hash运算得到的值
int hash;
/**
* Create new entry.
*/
Entry(int h,K k,V ,v,Entry<K,V> n){
value = v;
next = n;
key = k;
hash = h;
}
}
HashMap的存储结构如下图所示:
图中的每一个方格就表示一个Entry<K,V>对象,其中的横向则构成一个Entry<K,V>[] table数组,而竖向则是由Entry<K,V>的next属性形成的链表。
HashMap在添加元素(put)时的第一步就是计算该元素的key的hash值,用到如下方法:
HashMap对于key的重复性判断是基于两个内容的判断,一个就是hash值是否一样(会演变成key的hashCode是否一样),另一个就是equals方法是否一样(引用一样则肯定一样)。e.hash == hash && ((k = e.key) == key || key.equals(k))
。
hashCode重写的原则:当equals方法返回true,则两个对象的hashCode必须一样。
equals()方法在get()方法中的使用:
obj1.equals(obj2)=true
,则它们的hashCode必须相同,但如果两个对象不同,则它们的 hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,hashCode()方法目的纯粹用于提高效率,所以尽量定义好的 hashCode()方法,能加快哈希表的操作。如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时覆写(重载)equals方法和hashCode方法,而不要只写其中一个。