equals和hashCode之间的关系

前言

equals() 和 hashCode() 是 Java 的超级祖先类 Object 定义的两个重要的方法:

public boolean equals(Object obj)
public int hashCode()

讲道理,单从方法的定义上来看,equals() 和 hashCode() 这两个方法之间没有任何亲戚关系,远房都够不上资格。但往深处扒拉,它们之间还真的是有千丝万缕的关系。到底是什么关系呢?如果大家伙比较感兴趣的话,就请随我来,打怪进阶喽!

01、equals()

为了勾起大家的好奇心,我特意编写了下面这个示例。

public class Cmower {
    private String name;

    public Cmower(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (!(obj instanceof Cmower))
            return false;
        if (obj == this)
            return true;
        return this.name.equals(((Cmower) obj).name);
    }

    public static void main(String[] args) {
        Cmower a1 = new Cmower("沉默王二");
        Cmower a2 = new Cmower("沉默王三");

        Map<Cmower, Integer> m = new HashMap<Cmower, Integer>();
        m.put(a1, 18);
        m.put(a2, 28);
        System.out.println(m.get(new Cmower("沉默王二")));
    }
}

执行结果:

null

结果为什么会是null,却不是我们想要取得的值,那究竟是哪里出了错呢?

我们分析一下:
HashMap 之所以能够更快地通过键获取对应的值,是因为它的键位上使用了哈希码。当我们需要从 HashMap 中获取一个值的时候,会先把键转成一个哈希码,判断值所在的位置;然后在使用“==”操作符或者 equals() 方法比较键位是否相等,从而取出键位上的值。

可以查看一下 HashMap 类的 get() 方法的源码:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

在上例中,之所以没有从 HashMap 中取出 name 为“沉默王二”的 Cmower 对象,就是因为 put 的时候和 get 的时候使用的是两个对象,因此它们的哈希码不同而造成的。

开门见山地说吧,问题出在 hashCode() 身上,Cmower 类没有重写该方法。借此机会交代一下 equals() 和 hashCode() 这两个方法之间的关系吧:

1)如果两个对象需要相等(equals),那么它们必须有着相同的哈希码(hashCode);

2)但如果两个对象有着相同的哈希码,它们却不一定相等。

02、hashCode()

明白了原因之后,我们就可以对 Cmower 类进行改造,来看重写后的 hashCode() 吧。

@Override
public int hashCode() {
    return this.name.hashCode();
}

重写后的 hashCode() 方法体非常简单:返回 name 字段的哈希码。这样的话,put 和 get 用到的哈希码就是相同的,因为“沉默王二”的哈希码是 867758096。再次运行程序,你就会发现输出结果不再是 null 而是 18 了。

03、小结

1)equals() 的作用是用来判断两个对象是否相等。

2)hashCode() 的作用是获取对象的哈希码;哈希码一般是一个整数,用来确定对象在哈希表(比如 HashMap)中的索引位置。

拿 HashMap 来说,它本质上是通过数组实现的。当我们要获取某个“值”时,实际上是要获取数组中的某个位置的元素。而数组的位置,就是通过“键”来获取的;更进一步说,是通过“键”对应的哈希码计算得到的。

3)如果两个对象需要相等(equals),那么它们必须有着相同的哈希码(hashCode);

4)但如果两个对象有着相同的哈希码,它们却不一定相等。

(本人理解注:抛开HashMap这些基于hash的集合类来讨论的话,equals和hashCode本质上是没啥关系的,所以上述的两个结论也就不成立)

来对照一下官方给出的关于 equals() 和 hashCode() 的解释:

  • If two objects are equal according to the equals(Object) method, then
    calling the hashCode method on each of the two objects must produce
    the same integer result.
  • It is not required that if two objects are unequal according to the
    equals(java.lang.Object) method, then calling the hashCode method on
    each of the two objects must produce distinct integer results.
    However, the programmer should be aware that producing distinct
    integer results for unequal objects may improve the performance of
    hash tables.

可能有读者会问:“一定要同时重写 equals() 和 hashCode() 吗?”

回答当然是否定的。如果对象作为键在哈希表中,那么两个方法都要重写,因为 put 和 get 的时候需要用到哈希码和 equals() 方法;

如果对象不在哈希表中,仅用来判断是否相等,那么重写 equals() 就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值