重写equals()为什么要重写hashCode()

Object的通用方法

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

因为Object是类层次的根,所以每个类都默认含有以上方法。

==与equals方法

对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
对于引用类型,== 判断两个变量是否引用同一个对象,即判断两个引用指向的对象在内存中的地址是否一样,equals()默认和==一样。

  public boolean equals(Object obj) {
        return (this == obj);
    }

Integer因为重写了equals()方法,所以x与y引用的对象内容相等,所以返回true,但是他们在内存中的地址是不一样的,所以==返回fasle。

Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

为什么要重写hashcode()方法

 Note that it is generally necessary to override the {@code hashCode}
 method whenever this method is overridden, so as to maintain the
 general contract for the {@code hashCode} method, which states
 that equal objects must have equal hash codes.

官方文档说明,通常需要覆盖{@code hashCode}方法,以便维护{@code hashCode}方法的常规契约,其中声明相等的对象必须有相等的哈希码。
为什么会有这个契约:
1.相同对象的hash值必须相等
2.hash值相等对象有可能不相等
3.对象equsals()相等,hash值必须相等
有些同学可能会问,为什么要这样规定,我认为这样规定只有在使用hash集合的时候才有意义。我们看一下hashMap的put方法。为了方便说用,采用的是jdk1.7的hashMap实现方法。

ublic V put(K key, V value) {
        //如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,
        //此时threshold为initialCapacity 默认是1<<4(24=16)
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
       //如果key为null,存储位置为table[0]或table[0]的冲突链上
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀
        int i = indexFor(hash, table.length);//获取在table中的实际位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        //如果该对应数据已存在,执行覆盖操作。用新value替换旧value,并返回旧value
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败
        addEntry(hash, key, value, i);//新增一个entry
        return null;
    }

可以看到,在插入一对键值对时,如果插入的桶已经有值了,会判断key对象是否相等,相等就会修改节点上的旧值,不等就会采用头查法,在头结点插入对象。
在判断key对象相等时,通过这句代码实现
e.hash == hash && ((k = e.key) == key || key.equals(k))
相等必须满足两个条件
1.key对象的hash值相等
2.key对象在内存中地址相同或者equals()方法相等(是不是和上面==与equals方法区别联系起来了)
所以在使用hash集合时,使用自己的对象当键值时,重写equals方法最好重写hashCode方法,不然相同对象会有不同的hash值,你认为key相等,实际上却不等。

猜你喜欢

转载自www.cnblogs.com/cmg219/p/12355808.html