你真的了解equal()和hashCode()?

本文从下面几个方面来分析:

1.equals() 的作用是什么?

2.equals() 与 == 的区别是什么?

3.hashCode() 的作用是什么?

4.hashCode() 和 equals() 之间有什么联系?

第1部分 equals() 的作用

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

- equals() 定义在JDK的Object.java中,通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。在Object.java中的源码如下:


public boolean equals(Object obj) {

    return (this == obj);

}

既然这个方法在Object中,这就意味着所有的对象都有这个方法,从源码中可以看出,默认的equals()与“==”是等价的。因此,我们通常会覆写equals()方法:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。 就是说:

1) 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。

2) 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。

第2部分 equals() 与 == 的区别是什么?

== : 它的作用是判断两个对象的内存地址(或说引用)地址是不是相等。即,判断两个对象是不是同一个对象(对于基本数据类型,如int,比较的值;对于引用类型,如Integer,比较的是引用地址,就是说它们是否指向同一个对象)。

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况(前面第1部分已详细介绍过):

      情况1,类没有覆写equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。

      情况2,类覆盖了equals()方法。一般,我们通过覆写equals()方法来判断两个对象的内容是否相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。典型的覆盖内容的equals()写法:

private static class Person {

        int age;

        String name;

        public String toString() {

            return "(" + name + ", " + age + ")";

        }

        /**

        * @desc 覆盖equals方法

        */

        @Override

        public boolean equals(Object obj) {

            if (obj == null) {

                return false;

            }

            //如果是同一个对象返回true,反之返回false

            if (this == obj) {

                return true;

            }

            //判断是否类型相同

            if (this.getClass() != obj.getClass()) {

                return false;

            }

            Person person = (Person) obj;

            return name.equals(person.name) && age == person.age;

        }

    }

 第3部分 hashCode() 的作用

      hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

    hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。

    虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建并某个“类的散列表”时,该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),类的hashCode() 没有作用。

      上面的散列表,指的是Java集合框架中有关散列表的类,如HashMap,Hashtable,HashSet。

      也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。从HashMap.put()源码很容易看出来:

public V put(K key, V value) {

    return putVal(hash(key), key, value, false, true);

}

static final int hash(Object key) {

    int h;

    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>>

16);

}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

              boolean evict) {

    Node<K,V>[] tab; Node<K,V> p; int n, i;

    if ((tab =

table) ==

null || (n = tab.length) ==

0)

        n = (tab = resize()).length;

    if ((p = tab[i = (n - 1) & hash]) == null)

        tab[i] = newNode(hash, key, value, null);

    else {

    //....限于篇幅这里省略部分源码,

//但不影响问题的分析

}

    ++modCount;

    if (++size >

threshold)

        resize();

    afterNodeInsertion(evict);

    return null;

}

第4部分 hashCode() 和 equals() 的关系

按照“类的用途”分两种情况

情况一、类不使用在散列表的数据结构中,如:HashMap、HashSet等

这种情况,该类的hashCode()与equals()没有任何半毛关系,就是说equals() 用来比较该类的两个对象是否相等。而hashCode() 则根本没有任何作用,所以,不用理会hashCode()

情况二、类的对象用在了类似HashMap、HashSet这样的散列表数据结构中

  在这种情况下,该类的“hashCode() 和 equals() ”是有关系的:

        1)、如果两个对象相等,那么它们的hashCode()值一定相同。

              这里的相等是指,通过equals()比较两个对象时返回true(再次强调说明:equals()返回true,意味着要么指向的是同一个对象,要么内容一致)。

        2)、如果两个对象hashCode()相等,它们并不一定相等。

        因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值可能相等”,这就是哈希冲突

恶补:为什么会有产生冲突?简单的说,hash法是一种映射算法,一种压缩算法,很简单的一个场景,试想一下,你们班上有30个同学,其中只有5个女生,这不是僧多粥少吗?一个萝卜能有一个坑吗?能不冲突吗?!)。

        此外,在这种情况下。若要判断两个对象是否相等,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效。

例如,创建Person类的HashSet集合,必须同时覆盖Person类的equals() 和 hashCode()方法。

        如果单单只是覆盖equals()方法。我们会发现,equals()方法没有达到我们想要的效果。

总之:{obj1.equals(obj2)}==》{(obj1.hashCode())  == obj2.hashCode()},对象相等与对象的hashCode相等是充分非必要条件

效果分析:

import java.util.HashSet;

/**

*

* @author java栈长

* @desc 比较equals() 返回true 或 false时, hashCode()的值。

* <p>

* debug时,注释Person类的hashCode(),查看输出结果:

* <p>

* 一、不覆写hashCode(): //debug时注释hashCode()即可

* 输出

* p1.equals(p2) : true; p1(1174361318) p2(589873731)

* set:[(eee, 100), (eee, 100), (aaa, 200)]  //hashSet中出现重复元素,因为p1和p2的hashCode()不相同

* <p>

* 二、覆写Person类的hashCode() ------>正确姿势

* 输出:

* p1.equals(p2) : true; p1(37) p2(37)

* set:[(a, 100), (b, 200)]  //hashSet中没有重复元素,p1和p2的hashCode相同了,equals()也生效了。

*/

public class HashCodeTest {

    public static void main(String[] args) {

        // 新建Person对象,

        Person p1 =

new Person("a", 100);

        Person p2 =

new Person("a", 100);

        Person p3 =

new Person("b", 200);

        // 新建HashSet对象

        HashSet set =

new HashSet();

        set.add(p1);

        set.add(p2);

        set.add(p3);

        // 比较p1 和 p2, 并打印它们的hashCode()

        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());

        // 打印set

        System.out.printf("set:%s\n", set);

    }

    /**

    * @desc Person类。

    */

    private static class Person {

        int age;

        String

name;

        @Override

        public int hashCode() {

            int nameHash =

name.toUpperCase().hashCode();

            return nameHash ^

age;

        }

        public Person(String name, int age) {

            this.name = name;

            this.age = age;

        }

        public String

toString() {

            return "(" +

name +

", " +

age +

")";

        }

        /**

        * @desc 覆盖equals方法

        */

        @Override

        public boolean equals(Object obj) {

            if (obj ==

null) {

                return false;

            }

            //如果是同一个对象返回true,反之返回false

            if (this == obj) {

                return true;

            }

            //判断是否类型相同

            if (this.getClass() != obj.getClass()) {

                return false;

            }

            Person person = (Person) obj;

            return name.equals(person.name) &&

age == person.age;

        }

    }

}

输出结果分析:

一、不覆写hashCode(): //debug时注释hashCode()即可

输出:

p1.equals(p2) : true; p1(1174361318) p2(589873731)

set:[(eee, 100), (eee, 100), (aaa, 200)]  //hashSet中出现重复元素,因为p1和p2的hashCode()不相同,hashSet中hashCode不的

思考:p1和p2的hashCode为什么不同,jvm是如何生成这个hashCode的?

请移步:《java类的hashCode》一文

二、覆写hashCode() ------>正确姿势

输出:

p1.equals(p2) : true; p1(37) p2(37)

set:[(a, 100), (b, 200)]  //hashSet中没有重复元素,p1和p2的hashCode相同了,equals()也生效了。

更多干货在公号【java栈长】!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容