zoukankan      html  css  js  c++  java
  • HashMap中使用自定义类作为Key时,为何要重写HashCode和Equals方法

    之前一直不是很理解为什么要重写HashCode和Equals方法,才只能作为键值存储在HashMap中。通过下文,可以一探究竟。

     

    首先,如果我们直接用以下的Person类作为键,存入HashMap中,会发生发生什么情况呢?

     

    public class Person {

     

        private String id;

     

        public Person(String id) {

            this.id = id;

        }

    }

    import java.util.HashMap;

     

    public class Main {

        public static void main(String[] args) {

     

            HashMap<Person, String> map = new HashMap<Person, String>();

            map.put(new Person("001"), "findingsea");

            map.put(new Person("002"), "linyin");

            map.put(new Person("003"), "henrylin");

            map.put(new Person("003"), "findingsealy");

     

            System.out.println(map.toString());

     

            System.out.println(map.get(new Person("001")));

            System.out.println(map.get(new Person("002")));

            System.out.println(map.get(new Person("003")));

        }

    }

    那么输出结果是什么呢?

    {Person@6e4d4d5e=henrylin, Person@275cea3=findingsea, Person@15128ee5=findingsealy, Person@4513098=linyin}

    null

    null

    null

     

    我们可以看到,这里出现了两个问题:

    1. 在添加的过程中,我们将key=new Person("003")的键值对添加了两次,那么在期望中,HashMap中应该只存在一对这样的键值对,因为key(期望中)是相同的,所以不应该重复添加,第二次添加的value="findingsealy"应该替换掉原先的value="henrylin"。但是在输入中,我们发现期望中的情况并没有出现,而是在HashMap同时存在了value="findingsealy"和value="henrylin"的两个键值对,并且它们的key值还是不相同的,这显然是错误的。
    2. 在获取value值时,我们分别用三个Person对象去查找,这三个对象和我们刚刚存入的三个key值(在期望中)是相同的,但是查找出的却是三个null值,这显然也是错误的。

    那么,正确的方法其实在很多地方都是被描述过了,直接对Person类进行修改,重载equals和hashCode方法,修改过后的Person类如下:

    public class Person {

     

        private String id;

     

        public Person(String id) {

            this.id = id;

        }

     

        @Override

        public boolean equals(Object o) {

            if (this == o) return true;

            if (o == null || getClass() != o.getClass()) return false;

     

            Person person = (Person) o;

     

            if (id != null ? !id.equals(person.id) : person.id != null) return false;

     

            return true;

        }

     

        @Override

        public int hashCode() {

            return id != null ? id.hashCode() : 0;

        }

    }

    那么,当我们重新执行上述的检验程序时,得到的结果如下:

    {Person@ba31=findingsea, Person@ba32=linyin, Person@ba33=findingsealy}

    findingsea

    linyin

    findingsealy

    可以看到,之前指出的亮点错误都得到了改正。那么,为什么会这样呢?

    在HashMap中,查找key的比较顺序为:

    1. 计算对象的Hash Code,看在表中是否存在。
    2. 检查对应Hash Code位置中的对象和当前对象是否相等。

    显然,第一步就是要用到hashCode()方法,而第二步就是要用到equals()方法。在没有进行重载时,在这两步会默认调用Object类的这两个方法,而在Object中,Hash Code的计算方法是根据对象的地址进行计算的,那两个Person("003")的对象地址是不同的,所以它们的Hash Code也不同,自然HashMap也不会把它们当成是同一个key了。同时,在Object默认的equals()中,也是根据对象的地址进行比较,自然一个Person("003")和另一个Person("003")是不相等的。

    理解了这一点,就很容易搞清楚为什么需要同时重载hashCode()和equals两个方法了。

    • 重载hashCode()是为了对同一个key,能得到相同的Hash Code,这样HashMap就可以定位到我们指定的key上。
    • 重载equals()是为了向HashMap表明当前对象和key上所保存的对象是相等的,这样我们才真正地获得了这个key所对应的这个键值对。

    还有一个细节,在Person类中对于hashCode()的重在方法为:

    @Override

    public int hashCode() {

        return id != null ? id.hashCode() : 0;

    }

    这里可能有疑惑的点在于:为什么可以用String类型的变量的Hash Code作为Person类的Hash Code值呢?这样new Person(new String("003"))和new Person(new String("003"))的Hash Code是相等的吗?

    来看看以下代码的输出:

    System.out.println("findingsea".hashCode());

    System.out.println("findingsea".hashCode());

    System.out.println(new String("findingsea").hashCode());

    System.out.println(new String("findingsea").hashCode());

    728795174

    728795174

    728795174

    728795174

    可以看到四条语句的输出都是相等的,很直观的合理的猜测就是String类型也重载了hashCode()以根据字符串的内容来返回Hash Code值,所以相同内容的字符串具有相同的Hash Code。

    同时,这也说明了一个问题:为什么在已知hashCode()相等的情况下,还需要用equals()进行比较呢?就是因为避免出现上述例子中的出现的情况,因为根据对Person类的hashCode()方法的重载实现,Person类会直接用id这个String类型成员的Hash Code值作为自己的Hash Code值,但是很显然的,一个Person("003")和一个String("003")是不相等的,所以在hashCode()相等的情况下,还需要用equals()进行比较。

    以下例子可以作为上述说明的佐证:

    System.out.println(new Person("003").hashCode()); // 47667

    System.out.println(new String("003").hashCode()); // 47667

     

    System.out.println(new Person("003").equals(new String("003"))); // false

    以上即是全部。

     参考:https://segmentfault.com/a/1190000002655085

     

  • 相关阅读:
    TP5 中通过Request获取到的 Action不同的环境,大小写不一样
    正则符号
    ubuntu mysql卸载重装
    ubuntu 多域名配置
    微信小程序-开发入门(一)
    Maven通过profiles多环境配置打包
    GitLab多机备份与恢复操作
    动态增加linux目录大小
    MQ队列及常见操作
    ②将SVN迁移到GitLab-多分支多标签迁移
  • 原文地址:https://www.cnblogs.com/Jashinck/p/10618677.html
Copyright © 2011-2022 走看看