HashMap中使用自定义类做为Key时,为什么要重写HashCode和Equals方法

以前一直不是很理解为何要重写HashCode和Equals方法,才只能做为键值存储在HashMap中。经过下文,能够一探究竟。java

 

首先,若是咱们直接用如下的Person类做为键,存入HashMap中,会发生发生什么状况呢?segmentfault

 

public class Person {ide

 

    private String id;this

 

    public Person(String id) {spa

        this.id = id;对象

    }字符串

}get

import java.util.HashMap;hash

 

public class Main {class

    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

相关文章
相关标签/搜索