1.5分彩人工计划_为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

  • 时间:
  • 浏览:0
  • 来源:乌云菠萝格 - 专注共享糖豆娱乐网活动

     我在面试 Java初级开发的事先 ,总是会问:你有这麼 重写过hashcode办法 ?不少候选人直接说没写过。让他想,或许真的没写过,于是就再通过另两个大现象确认:你在用HashMap的事先 ,键(Key)每段,有这麼 放过自定义对象?而你是什么 事先 ,候选人说放过,于是另两个大现象的回答就自相矛盾了。

    最近问下来,你是什么 大现象普遍回答不大好,于是在本文里,就干脆从hash表讲起,讲述HashMap的存数据规则,由此当当当我们 歌词 就自然清楚上述大现象的答案了。

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

    当当当我们 歌词 先复习数据形状里的另两个知识点:在另两个长度为n(假设是300)的线性表(假设是ArrayList)里,存放着无序的数字;事先 当当当我们 歌词 不 找另两个指定的数字,就不得不通过从头到尾依次遍历来查找,原来的平均查找次数是n除以2(这里是300)。

当当当我们 歌词 再来观察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对象的内存地址(假设是30)。

    

    事先 当当当我们 歌词 就让 是调用hm.get(k1),这麼 当当当我们 歌词 会再次调用hashCode办法 (还是返回k1的地址30),就让 根据得到的hash值,能调快地找到k1。

    但当当当我们 歌词 这里的代码是hm.get(k2),当当当我们 歌词 都调用Object类的hashCode办法 (事先 Key里没定义)计算k2的hash值时,实在 得到的是k2的内存地址(假设是30)。事先 k1和k2是另两个不同的对象,统统统统它们的内存地址一定不要再相同,也统统统统 说它们的hash值一定不同,这统统统统 当当当我们 歌词 无法用k2的hash值去拿k1的导致 。

    当当当我们 歌词 都把第16和17行的hashCode办法 的注释打上去后,会发现它是返回id属性的hashCode值,这里k1和k2的id都不 1,统统统统它们的hash值是相等的。

    当当当我们 歌词 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是30,把k1对象倒入到对应的位置。而取k2时,是先计算它的hash值(事先 k2的id也是1,你是什么 值也是30),就让 到你是什么 位置去找。

    但结果会出乎当当当我们 歌词 意料:明明30号位置事先 有k1,但第26行的输出结果依然是null。其导致 统统统统 这麼 重写Key对象的equals办法 。

    HashMap是用链地址法来补救冲突,也统统统统 说,在30号位置上,有事先 占据 着多个用链表形式存储的对象。它们通过hashCode办法 返回的hash值都不 30。

     当当当我们 歌词 都通过k2的hashCode到30号位置查找时,实在 会得到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核心技术及面试指南这本书中相关内容改编而来。