为什么重写equals方法同时一定要重写hashCode方法

问题:为什么重写equals方法一定要同时重写hashCode方法?

首先抛出答案:这和集合的某些操作有关(比如HashSet的add方法)

HashSet add(E e) 方法如下:

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
}
复制代码

在HashSet的内部维护了一个HashMap,可以看到对于HashSet的add操作委托给了HashMap的put 操作。

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;
    //内部维护的HashMap
    private transient HashMap<E,Object> map;
复制代码

继续查看HashMap的put方法如下:

public V put(K key, V value) {
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
        addEntry(hash, key, value, i);
        return null;
    }
复制代码

我们大致分析一下这段代码逻辑:

  1. 首先根据添加元素的hash值寻找到可以放置的Entry数组的位置。
  2. 然后在这个合适的位置上根据Entry链查找是否有equals相同的值,如果有就返回旧值,如果没有就插入这个HashMap。(注意:如果这个Entry链不存在则直接创建entry并插入这个合适的位置,如果entry链只有一个entry节点,那么直接插入)

我们再来看看HashMap的hash方法如下:

  final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
复制代码

我们可以看到h ^= k.hashCode();这个调用,这也正好是调用了元素的hashCode方法。

重点来了:如果我们不重写hashCode方法会产生什么样的结果?

我们回头看看HashMap的put方法,如果这个时候重复添加具有相同内容的对象,如果不重写hashCode,那么这俩个对象就有可能会在Entry数组的不同位置分别构建entry,然而它们的内容却是一致的,即使你重写了equals方法并不一定能保证HashSet中的元素唯一。

所以,对于集合操作比如Set类型接口,Map类型接口而言,重写了equals方法一定要重写hashCode 保证元素唯一性。


而对于equals使用的准则有:

自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。

对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。

传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true, 并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。

一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false, 前提是对象上 equals 比较中所用的信息没有被修改。

非空性:对于任何非空引用值 x,x.equals(null) 都应返回 false。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值