幸运飞艇一分钟规律_为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

  • 时间:
  • 浏览:2
  • 来源:海南新闻网_海南主流媒体_海南门户网站

     我在面试 Java初级开发的就让,无缘无故 会问:你有没人 重写过hashcode法子?不少候选人直接说没写过。让你想,或许真的没写过,于是就再通过有有3个 疑问确认:你在用HashMap的就让,键(Key)部分,有没人 放过自定义对象?而你你你這個就让,候选人说放过,于是有有3个 疑问的回答就自相矛盾了。

    最近问下来,你你你這個疑问普遍回答不大好,于是在本文里,就干脆从hash表讲起,讲述HashMap的存数据规则,由此亲戚亲戚村里人 都歌词 歌词 就自然清楚上述疑问的答案了。

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

    亲戚亲戚村里人 都歌词 歌词 先复习数据特性里的有有3个 知识点:在有有3个 长度为n(假设是500000)的线性表(假设是ArrayList)里,存放着无序的数字;机会亲戚亲戚村里人 都歌词 歌词 要找有有3个 指定的数字,就不得不通过从头到尾依次遍历来查找,原来的平均查找次数是n除以2(这里是50000)。

亲戚亲戚村里人 都歌词 歌词 再来观察Hash表(这里的Hash表纯粹是数据特性上的概念,和Java无关)。它的平均查找次数接近于1,代价相当小,关键是在Hash表里,存倒入其中的数据和它的存储位置是用Hash函数关联的。

    亲戚亲戚村里人 都歌词 歌词 假设有有3个 Hash函数是x*x%5。当然实际请况里不机会用没人 简单的Hash函数,亲戚亲戚村里人 都歌词 歌词 这里纯粹为了说明方便,而Hash表是有有3个 长度是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的对象建立有有3个 同义词链表。假设亲戚亲戚村里人 都歌词 歌词 在倒入8的就让,发现4号位置机会被占,没人 就会新建有有3个 链表结点倒入8。同样,机会亲戚亲戚村里人 都歌词 歌词 要找8,没人 发现4号索引里不会 8,那会沿着链表依次查找。

    觉得 亲戚亲戚村里人 都歌词 歌词 还是无法彻底处置Hash值冲突的疑问,但会 Hash函数设计合理,仍能保证同义词链表的长度被控制在有有3个 合理的范围里。这里讲的理论知识难能可贵无的放矢,亲戚亲戚村里人 都歌词 歌词 能在后文里清晰地了解到重写hashCode法子的重要性。

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

    当亲戚亲戚村里人 都歌词 歌词 用HashMap存入自定义的类时,机会不重写你你你這個自定义类的equals和hashCode法子,得到的结果会和亲戚亲戚村里人 都歌词 歌词 预期的不一样。亲戚亲戚村里人 都歌词 歌词 来看WithoutHashCode.java你你你這個例子。

在其中的第2到第18行,亲戚亲戚村里人 都歌词 歌词 定义了有有3个 Key类;在其中的第3行定义了唯一的有有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行,亲戚亲戚村里人 都歌词 歌词 定义了有有3个 Key对象,它们的id不会 1,就好比它们是两把相同的都能打开同一扇门的钥匙。

    在第24行里,亲戚亲戚村里人 都歌词 歌词 通过泛型创建了有有3个 HashMap对象。它的键部分可不还要存放Key类型的对象,值部分可不还要存储String类型的对象。

    在第25行里,亲戚亲戚村里人 都歌词 歌词 通过put法子把k1和一串字符倒入到hm里; 而在第26行,亲戚亲戚村里人 都歌词 歌词 想用k2去从HashMap里得到值;这就好比亲戚亲戚村里人 都歌词 歌词 想用k1这把钥匙来锁门,用k2来开门。这是符合逻辑的,但从当前结果看,26行的返回结果不会 亲戚亲戚村里人 都歌词 歌词 想象中的那个字符串,只是 null。

    由于有有有3个 —没人 重写。第一是没人 重写hashCode法子,第二是没人 重写equals法子。

   当亲戚亲戚村里人 都歌词 歌词 往HashMap里放k1时,首先会调用Key你你你這個类的hashCode法子计算它的hash值,就让把k1倒入hash值所指引的内存位置。

    关键是亲戚亲戚村里人 都歌词 歌词 没人 在Key里定义hashCode法子。这里调用的仍是Object类的hashCode法子(所有的类不会 Object的子类),而Object类的hashCode法子返回的hash值觉得 是k1对象的内存地址(假设是50000)。

    

    机会亲戚亲戚村里人 都歌词 歌词 就让是调用hm.get(k1),没人 亲戚亲戚村里人 都歌词 歌词 会再次调用hashCode法子(还是返回k1的地址50000),就让根据得到的hash值,能变快地找到k1。

    但亲戚亲戚村里人 都歌词 歌词 这里的代码是hm.get(k2),当亲戚亲戚村里人 都歌词 歌词 调用Object类的hashCode法子(机会Key里没定义)计算k2的hash值时,觉得 得到的是k2的内存地址(假设是5000)。机会k1和k2是有有3个 不同的对象,什么都有有它们的内存地址一定不需要相同,也只是 说它们的hash值一定不同,这只是 亲戚亲戚村里人 都歌词 歌词 无法用k2的hash值去拿k1的由于。

    当亲戚亲戚村里人 都歌词 歌词 把第16和17行的hashCode法子的注释加进去后,会发现它是返回id属性的hashCode值,这里k1和k2的id不会 1,什么都有有它们的hash值是相等的。

    亲戚亲戚村里人 都歌词 歌词 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是5000,把k1对象倒入到对应的位置。而取k2时,是先计算它的hash值(机会k2的id也是1,你你你這個值也是5000),就让到你你你這個位置去找。

    但结果会出乎亲戚亲戚村里人 都歌词 歌词 意料:明明5000号位置机会有k1,但第26行的输出结果依然是null。其由于只是 没人 重写Key对象的equals法子。

    HashMap是用链地址法来处置冲突,也只是 说,在5000号位置上,有机会占据 着多个用链表形式存储的对象。它们通过hashCode法子返回的hash值不会 5000。

     当亲戚亲戚村里人 都歌词 歌词 通过k2的hashCode到5000号位置查找时,觉得 会得到k1。但k1有机会仅仅是和k2具有相同的hash值,但难能可贵和k2相等(k1和k2两把钥匙难能可贵能开同一扇门),你你你這個就让,就还要调用Key对象的equals法子来判断两者算不算相等了。

    机会亲戚亲戚村里人 都歌词 歌词 在Key对象里没人 定义equals法子,系统就不得不调用Object类的equals法子。机会Object的固有法子是根据有有3个 对象的内存地址来判断,什么都有有k1和k2一定不需要相等,这只是 为哪此依然在26行通过hm.get(k2)依然得到null的由于。

    为了处置你你你這個疑问,亲戚亲戚村里人 都歌词 歌词 还要打开第9到14行equals法子的注释。在你你你這個法子里,只是 有有3个 对象不会 Key类型,但会 它们的id相等,它们就相等。

3 对面试疑问的说明

    机会在项目里无缘无故 会用到HashMap,什么都有有我在面试的就让不会 问你你你這個疑问∶你有没人 重写过hashCode法子?你在使用HashMap时有没人 重写hashCode和equals法子?你是为甚么写的?

    根据问下来的结果,我发现初级线程员对你你你這個知识点普遍没掌握好。重申一下,机会亲戚亲戚村里人 都歌词 歌词 要在HashMap的“键”部分存放自定义的对象,一定要在你你你這個对象里用我本人的equals和hashCode法子来覆盖Object里的同名法子。 

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