Java用自定义的类作为HashMap的key值
来源:互联网 发布:retas for mac 下载 编辑:程序博客网 时间:2024/05/17 21:40
这是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}nullnullnull
我们可以看到,这里出现了两个问题:
在添加的过程中,我们将
key=new Person("003")
的键值对添加了两次,那么在期望中,HashMap
中应该只存在一对这样的键值对,因为key
(期望中)是相同的,所以不应该重复添加,第二次添加的value="findingsealy"
应该替换掉原先的value="henrylin"
。但是在输入中,我们发现期望中的情况并没有出现,而是在HashMap
同时存在了value="findingsealy"
和value="henrylin"
的两个键值对,并且它们的key
值还是不相同的,这显然是错误的。在获取
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}findingsealinyinfindingsealy
可以看到,之前指出的亮点错误都得到了改正。那么,为什么会这样呢?
在HashMap
中,查找key
的比较顺序为:
- 计算对象的
Hash Code
,看在表中是否存在。 - 检查对应
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()
的重在方法为:
@Overridepublic 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());
728795174728795174728795174728795174
可以看到四条语句的输出都是相等的,很直观的合理的猜测就是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()); // 47667System.out.println(new String("003").hashCode()); // 47667System.out.println(new Person("003").equals(new String("003"))); // false
- Java用自定义的类作为HashMap的key值
- Java 自定义类作为HashMap的key
- Java用自定义的类型作为HashMap的key
- java HashMap用自定义类作为key
- java HashMap用自定义类作为key
- Java 将自定义的对象作为HashMap的key
- 自定义枚举类 Enum 是否可以作为 HashMap 的key
- 使用一个类作为hashMap的key
- equals和==的区别,以及如何将自定义类作为HashMap的Key
- java用自定义类型作为HashMap的键
- Java 用自定义类型作为HashMap的键
- 如何保证HashMap自定义类key 值的唯一性
- 空字符串可以作为HashMap的key
- 谨慎使用String作为HashMap的Key
- 谨慎使用String作为HashMap的Key
- 谨慎使用String作为HashMap的Key
- StringBuilder万万不可作为HashMap的key
- 如何把坐标作为hashmap的key
- Android月历控件(DatePicker)和时间控件(TimePicker)的使用
- 目录遍历漏洞
- AngularJS(四)——模块
- 【华为OJ】【007-取近似值】
- java用io流打包下载文件
- Java用自定义的类作为HashMap的key值
- O'Reilly 免费电子书
- Ruby - Nokogiri 解析XML的实例
- 城市列表
- 头部和正文标签
- 上下文-作用域-作用域链-闭包
- 一扫天下——ZXing使用全解析,基于ZXing3.1
- 前50名欠款人流血的国有银行
- d3.js——矩形分区图