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

  • 时间:
  • 浏览:0
  • 来源:苹果资源网 - 专注共享南街博客技术

     我在面试 Java初级开发的就说 我我,总是会问:你有没法 重写过hashcode妙招?不少候选人直接说没写过。你还里能想,或许真的没写过,于是就再通过就说 我我大问题确认:你在用HashMap的就说 我我,键(Key)偏离 ,有没法 放过自定义对象?而你你这一 就说 我我,候选人说放过,于是就说 我我大问题的回答就自相矛盾了。

    最近问下来,你你这一 大问题普遍回答不大好,于是在本文里,就干脆从hash表讲起,讲述HashMap的存数据规则,由此另一个人 就自然清楚上述大问题的答案了。

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

    另一个人 先复习数据社会形态里的就说 我我知识点:在就说 我我长度为n(假设是1000)的线性表(假设是ArrayList)里,存放着无序的数字;机会另一个人 要找就说 我我指定的数字,就不得不通过从头到尾依次遍历来查找,就说 我我的平均查找次数是n除以2(这里是1000)。

另一个人 再来观察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对象的内存地址(假设是100)。

    

    机会另一个人 并且是调用hm.get(k1),没法 另一个人 会再次调用hashCode妙招(还是返回k1的地址100),并且根据得到的hash值,能快一点 地找到k1。

    但另一个人 这里的代码是hm.get(k2),当另一个人 调用Object类的hashCode妙招(机会Key里没定义)计算k2的hash值时,我觉得 得到的是k2的内存地址(假设是100)。机会k1和k2是就说 我我不同的对象,什么都有有有它们的内存地址一定不想相同,也就说 我我说它们的hash值一定不同,这就说 我我另一个人 无法用k2的hash值去拿k1的原应。

    当另一个人 把第16和17行的hashCode妙招的注释去掉 后,会发现它是返回id属性的hashCode值,这里k1和k2的id与否 1,什么都有有有它们的hash值是相等的。

    另一个人 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是100,把k1对象倒进到对应的位置。而取k2时,是先计算它的hash值(机会k2的id也是1,你你这一 值也是100),并且到你你这一 位置去找。

    但结果会出乎另一个人 意料:明明100号位置机会有k1,但第26行的输出结果依然是null。其原应就说 我我没法 重写Key对象的equals妙招。

    HashMap是用链地址法来除理冲突,也就说 我我说,在100号位置上,有机会处于着多个用链表形式存储的对象。它们通过hashCode妙招返回的hash值与否 100。

     当另一个人 通过k2的hashCode到100号位置查找时,我觉得 会得到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核心技术及面试指南这本书中相关内容改编而来。