5分飞艇官方网站_为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

  • 时间:
  • 浏览:3

     我在面试 Java初级开发的前一天,老会 会问:你有找不到重写过hashcode辦法 ?不少候选人直接说没写过。我应该 想,或许真的没写过,于是就再通过兩个 多多哪此的间题确认:你在用HashMap的前一天,键(Key)每段,有找不到放过自定义对象?而這個前一天,候选人说放过,于是兩个 多多哪此的间题的回答就自相矛盾了。

    最近问下来,這個哪此的间题普遍回答不大好,于是在本文里,就干脆从hash表讲起,讲述HashMap的存数据规则,由此我们 就自然清楚上述哪此的间题的答案了。

1 通过Hash算法来了解HashMap对象的高效性

    我们 先复习数据底部形态里的兩个 多多知识点:在兩个 多多长度为n(假设是8000)的线性表(假设是ArrayList)里,存放着无序的数字;我应该 我们 要找兩个 多多指定的数字,就不得不通过从头到尾依次遍历来查找,兩个 多多的平均查找次数是n除以2(这里是8000)。

我们 再来观察Hash表(这里的Hash表纯粹是数据底部形态上的概念,和Java无关)。它的平均查找次数接近于1,代价相当小,关键是在Hash表里,存放满其中的数据和它的存储位置是用Hash函数关联的。

    我们 假设兩个 多多Hash函数是x*x%5。当然实际情况汇报里不我应该 用找不到简单的Hash函数,我们 这里纯粹为了说明方便,而Hash表是兩个 多多长度是11的线性表。我应该 我们 要把6放满其中,找不到我们 首先会对6用Hash函数计算一下,结果是1,统统我们 就把6放满到索引号是1這個位置。同样我应该 我们 要放数字7,经过Hash函数计算,7的结果是4,找不到它将被放满索引是4的這個位置。這個效果如下图所示。

    兩个 多多做的好处非常明显。比如我们 要从中找6這個元素,我们 都不能 先通过Hash函数计算6的索引位置,我应该 直接从1号索引里找到它了。

不过我们 会遇到“Hash值冲突”這個哪此的间题。比如经过Hash函数计算后,7和8会有相同的Hash值,对此Java的HashMap对象采用的是”链地址法“的解决方案。效果如下图所示。

 

    具体的做法是,为所有Hash值是i的对象建立兩个 多多同义词链表。假设我们 在放满8的前一天,发现4号位置我应该 被占,找不到就会新建兩个 多多链表结点放满8。同样,我应该 我们 要找8,找不到发现4号索引里后要 8,那会沿着链表依次查找。

    虽然 我们 还是无法彻底解决Hash值冲突的哪此的间题,我应该 Hash函数设计合理,仍能保证同义词链表的长度被控制在兩个 多多合理的范围里。这里讲的理论知识都是而是无的放矢,我们 能在后文里清晰地了解到重写hashCode辦法 的重要性。

2 为哪此要重写equals和hashCode辦法

    当我们 用HashMap存入自定义的类时,我应该 不重写這個自定义类的equals和hashCode辦法 ,得到的结果会和我们 预期的不一样。我们 来看WithoutHashCode.java這個例子。

在其中的第2到第18行,我们 定义了兩个 多多Key类;在其中的第3行定义了唯一的兩个 多多属性id。当前我们 先注释掉第9行的equals辦法 和第16行的hashCode辦法 。    

1	import java.util.HashMap;
2	class Key {
3		private Integer id;
4		public Integer getId() 
5	{return id; }
6		public Key(Integer id) 
7	{this.id = id;	}
8	//故意先注释掉equals和hashCode辦法

9	//	public boolean equals(Object o) {
10	//		if (o == null || !(o instanceof Key)) 
11	//		{ return false;	} 
12	//		else 
13	//		{ return this.getId().equals(((Key) o).getId());}
14	//	}
15		
16	//	public int hashCode() 
17	//	{ return id.hashCode();	}
18	}
19	
20	public class WithoutHashCode {
21		public static void main(String[] args) {
22			Key k1 = new Key(1);
23			Key k2 = new Key(1);
24			HashMap<Key,String> hm = new HashMap<Key,String>(); 
25			hm.put(k1, "Key with id is 1");		
26			System.out.println(hm.get(k2));		
27		}
28	}

    在main函数里的第22和23行,我们 定义了兩个 多多Key对象,它们的id后要 1,就好比它们是两把相同的都能打开同一扇门的钥匙。

    在第24行里,我们 通过泛型创建了兩个 多多HashMap对象。它的键每段都不能 存放Key类型的对象,值每段都不能 存储String类型的对象。

    在第25行里,我们 通过put辦法 把k1和一串字符放满到hm里; 而在第26行,我们 想用k2去从HashMap里得到值;这就好比我们 想用k1这把钥匙来锁门,用k2来开门。这是符合逻辑的,但从当前结果看,26行的返回结果后要 我们 想象中的那个字符串,我应该 null。

    原应兩个 多多多—找不到重写。第一是找不到重写hashCode辦法 ,第二是找不到重写equals辦法 。

   当我们 往HashMap里放k1时,首先会调用Key這個类的hashCode辦法 计算它的hash值,我应该 把k1放满hash值所指引的内存位置。

    关键是我们 找不到在Key里定义hashCode辦法 。这里调用的仍是Object类的hashCode辦法 (所有的类后要 Object的子类),而Object类的hashCode辦法 返回的hash值虽然 是k1对象的内存地址(假设是800)。

    

    我应该 我们 我应该 是调用hm.get(k1),找不到我们 会再次调用hashCode辦法 (还是返回k1的地址800),我应该 根据得到的hash值,能调快地找到k1。

    但我们 这里的代码是hm.get(k2),当我们 调用Object类的hashCode辦法 (我应该 Key里没定义)计算k2的hash值时,虽然 得到的是k2的内存地址(假设是800)。我应该 k1和k2是兩个 多多不同的对象,统统它们的内存地址一定不想相同,也我应该 说它们的hash值一定不同,这我应该 我们 无法用k2的hash值去拿k1的原应。

    当我们 把第16和17行的hashCode辦法 的注释去掉 后,会发现它是返回id属性的hashCode值,这里k1和k2的id后要 1,统统它们的hash值是相等的。

    我们 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是80,把k1对象放满到对应的位置。而取k2时,是先计算它的hash值(我应该 k2的id也是1,這個值也是80),我应该 到這個位置去找。

    但结果会出乎我们 意料:明明80号位置我应该 有k1,但第26行的输出结果依然是null。其原应我应该 找不到重写Key对象的equals辦法 。

    HashMap是用链地址法来解决冲突,也我应该 说,在80号位置上,有我应该 处在着多个用链表形式存储的对象。它们通过hashCode辦法 返回的hash值后要 80。

     当我们 通过k2的hashCode到80号位置查找时,虽然 会得到k1。但k1有我应该 仅仅是和k2具有相同的hash值,但都是而是和k2相等(k1和k2两把钥匙都是而是能开同一扇门),這個前一天,就不能 调用Key对象的equals辦法 来判断两者算不算相等了。

    我应该 我们 在Key对象里找不到定义equals辦法 ,系统就不得不调用Object类的equals辦法 。我应该 Object的固有辦法 是根据兩个 多多对象的内存地址来判断,统统k1和k2一定不想相等,这我应该 为哪此依然在26行通过hm.get(k2)依然得到null的原应。

    为了解决這個哪此的间题,我们 不能 打开第9到14行equals辦法 的注释。在這個辦法 里,我应该 我兩个 多多对象后要 Key类型,我应该 它们的id相等,它们就相等。

3 对面试哪此的间题的说明

    我应该 在项目里老会 会用到HashMap,统统我在面试的前一天后要 问這個哪此的间题∶你有找不到重写过hashCode辦法 ?你在使用HashMap时有找不到重写hashCode和equals辦法 ?你是如可写的?

    根据问下来的结果,我发现初级多线程 员对這個知识点普遍没掌握好。重申一下,我应该 我们 要在HashMap的“键”每段存放自定义的对象,一定要在這個对象里用个人的equals和hashCode辦法 来覆盖Object里的同名辦法 。 

     本文是从Java核心技术及面试指南这本书中相关内容改编而来。