动力节点旗下在线教育品牌  |  咨询热线:400-8080-105 学Java全栈,上蛙课网
首页 > 文章

equal和hashCode联系与区别

07-24 17:22 262浏览
举报 T字号
  • 大字
  • 中字
  • 小字

熟悉Object类的同学都知道,equals()和hashCode()都是Object类中的方法篇文章大家详细介绍equals()和hashCode()用途,以及equals()和hashCode()联系

其中equals()方法我们经常用到,比如String对象的比较str1.equals(str2)。两个方法本来没有任何耦合关系,但是把它和hashCode()方法放在一起总结是因为Map集合的实现让这两个方法产生了某种程度上的耦合。

hashCode()的作用是获取哈希码,也称为散列码它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

为了更好的理解,我们可以在脑海中构建一个场景:我们要去除一组数中重复的值,保留不相同的值。我们首先遍历,然后比较他们如果有相同的就干掉,普通的方式还得循环嵌套。如果把这组数换成一组对象的话,我们又如何去掉相同对象,内容相同的只保留一个呢。如果我们按照之前的方法,就得遍历数组,然后用get方法获取对象中的值,依次比较。这种方式不仅很蠢而且很慢,我们能不能像比较数字那样去比较对象呢?只要我们保证内容相同的对象生成相同的整数就好了,这样就好比较了,这个整数就是hashCode。

hashCode会出现冲突,什么叫冲突,就是我们不能保证不同的对象生成的不同的hashCode,有可能不同的对象生成的hashCode是相同的,这时候我们怎么区别呢,那么我们就需要比较对象的所有字段了,就是重写equals方法,在equals方法中去比较两个对象的所有字段。散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

由于每一个 Java 类都继承自 Object 类,所以每一个对象都具有equals这个方法。Object 类中定义的 equals(Object)方法是直接使用「==」运算符比较的两个对象,所以在没有覆盖 equals(Object)方法的情况下,equals(Object)与「==」运算符一样,比较的是引用相比「==」运算符,equals(Object)方法的特殊之处就在于它可以被覆盖,所以可以通过覆盖的方法让它不是比较引用而是比较数据内容,例如 String 类的 equals 方法是用于比较两个独立对象的内容是否相同,即堆中的内容是否相同

public static void main(String[] args) {

    String str1 = new String("Hello world");

    String str2 = new String("Hello world");

    System.out.println(str1 == str2);        // false

    System.out.println(str1.equals(str2));   // true

}

实际情况下我们是可以根据str2来找到散列表中key为str1的元素的,如果只使用==比较来判断key是否匹配是不可行的,因为他们在堆上的地址并不一样,所以得使用equals()来进一步比较。 equals()在Object中默认的实现其实也是使用==比较:

public boolean equals(Object obj) {

    return (this == obj);

}

之所以我们可以直接使用String的equals(),是因为String类重写了equals(),具体实现就是比较两个字符串对象底层的char数组存放的字符是否完全一样。所以在我们要比较自定义的类的对象是否相等的时候,我们要根据自己的需求主动重写equals(),重写equals()也是我们能够把自定义的类作为散列容器中key的类型的先决条件。

equals() 和 hashCode()的关系:

Effective Java 3rd edition

Item 11 : Always override hashCode when you override equals

HashMap的源码得出,当从map中找一个指定的key时,我们首先是根据这个key的hashCode()的返回值经过处理后生成的hash值找到对应的桶,再遍历这个桶找到相等的key。

从以上过程可以提炼出非常重要的一点,相等的对象hashCode()返回值一定要相等,因为如果hashCode()返回值不相等的话,计算出来的hash值很可能是不相等的,这会直接导致相同的key可能放入不同的桶中,这是不被允许的。所以如果我们要用自定义类对象做散列表的key时,在重写了equals()后,还要重写hashCode()来保证equals()返回为true的情况下两个对象hashCode()的返回值也相等。

实例分析:

public class Test {

    public static void main(String[] args) {

        HashMap map = new HashMap<>();

        // 生成两个相等的对象

        People p1 = new People(11, "Jack");

        People p2 = new People(11, "Jack");

 

        map.put(p1, 1);

        // 当没重写hashCode()时输出的是null

        // 重写时输出的是 1

        System.out.println(map.get(p2));

    }

}   

 

// 自定义People类

class People {

    int age;

    String name;

 

    public People(int age, String name) {

        this.age = age;

        this.name = name;

    }

 

    // 我们认为只要两个People对象age和name相等则这两个对象相等

    @Override

    public boolean equals(Object obj) {

        return ((People) obj).age == age && ((People) obj).name.equals(name);

    }

 

    // equals()返回为true时,两个对象的hashCode()返回值也是一样的

    @Override

    public int hashCode() {

        return age * name.hashCode();

    }

}

为了避免在使用散列容器出现错误,当我们重写自定义类的equals()方法时,还要重写hashCode()方法,以保证相等的对象hashCode()返回值必定相等。hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

既然hashCode()效率这么高为什么还要equals()呢?因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出结论:equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。

hashCode和equals之间的区别在java编程实战中很容易遇见的问题,也java面试题中的高频问题。无论是为了学习还是工作,我们都应该也必须要明辨hashCode和equals的用法差异。差之毫厘,谬以千里,看似相同的两者在实际中如果用混了可能会造成整个程序的错误,使得程序无法运行。

0人推荐
共同学习,写下你的评论
0条评论
无心出岫
程序员无心出岫

10篇文章贡献51071字

作者相关文章更多>

推荐相关文章更多>

DOM渲染的详细过程

QCode09-04 14:38

CSS水平和垂直居中技巧大梳理

Code大师09-04 14:50

mui的input框在IOS系统下无法聚焦或点击多次才能聚焦

不写代码你养我啊08-23 11:14

推荐的-视.频播放器以及在线客服

不写代码你养我啊09-17 18:02

谈谈java多线程的三大特性

要学习了06-18 18:13

发评论

举报

0/150

取消