欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java用自定义的类作为HashMap的key值实例

程序员文章站 2024-03-09 13:05:47
这是java中很经典的问题,在面试中也经常被问起。其实很多书或者文章都提到过要重载hashcode()和equals()两个方法才能实现自定义键在hashmap中的查找,但...

这是java中很经典的问题,在面试中也经常被问起。其实很多书或者文章都提到过要重载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

以上这篇java用自定义的类作为hashmap的key值实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。