作为Java小白,equals和hashCode的这点关系,你不得不知道啊!
来源:     阅读:619
云上智慧
发布于 2020-04-24 17:55
查看主页

在逛 programcreek 的时候,我发现了少量专注细节但价值连城的主题。比方说:Java 的 equals()hashCode() 是远房亲戚吗?像这类灵魂拷问的主题,非常值得深入地研究一下。

另外,我想要告诉大家的是,研究的过程非常的有趣,就如同在迷宫里探宝一样,起初有些不知所措,但经过一番用心的摸索后,不但会找到宝藏,还会有一种茅塞顿开的感觉,非常棒。

对于绝大多数的初级程序员或者者说不重视“内功”的老鸟来说,往往停留在“知其然不知其所以然”的层面上——会用,但要说底层的原理,可就只能挠挠头双手一摊一张问号脸了。

很长一段时间内,我,沉默王二也一直处于这种层面上。但我决定改变了,由于“内功”就如同是在打地基,只有把地基打好了,才能盖起经得住考验的高楼大厦。借此机会,我就和大家一起,对“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("沉默王二")));    }}

我们定义了一个 Cmower 类,它有一个字段 name;而后我们重写了 equals() 方法:

1)假如指定对象为 null,返回 false;

2)假如指定对象的类型不是 Cmower 类,返回 false;

3)假如指定对象“==”当前对象,返回 true;

4)假如指定对象的 name 和当前对象的 name 相等,返回 true;意味着只需 name 相等,两个对象就是 equals 的。

在 main 方法中,我们创立了两个 Cmower 类型的对象,name 分别为“沉默王二”和“沉默王三”,并将它们作为键放入了 HashMap 当中;按理说,只需键的 name 为“沉默王二”,程序就应该能够获取我们之前放入的 Cmower 对象。但结果却“出人预料”:

null

可明明 HashMap 中放入了“沉默王二”啊,debug 也可以证实这一点。

那到底是哪里出了错呢?

02、hashCode()

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

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

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

这就如同在说:你和你对象要想在一起天长地久,就必需彼此相爱;但即使你和你对象彼此相爱,但却不肯定真的能在一起。

(扎心了,老铁)

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 的时候两个对象的哈希码不同的起因造成的。

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

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

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

03、小结

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

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

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

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

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

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

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

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

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

嘿嘿??,这下搞清楚 equals()hashCode() 之间的关系了吧!

04、鸣谢

好了,读者朋友们,以上就是本文的一律内容了。能看到这里的都是最优秀的程序员,升职加薪就是你了??。原创不易,假如觉得有点用的话,请不要吝啬你手中点赞的权力——由于这将是我写作的最强动力

免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 系统环境 服务器应用
相关推荐
CSS处理盒模型居中的问题
Nginx反代Mogilefs分布式储存示例
【NGINX入门】7.Nginx配置安全证书SSL实例
新来实习生太惨,所以整理一点传统企业到互联网企业必备可收藏。
网页设计:HTML标签语义化
首页
搜索
订单
购物车
我的