台湾生活网_台湾人的网上生活家园 -

时时彩导航网_为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

  • 时间:
  • 浏览:13

     我在面试 Java初级开发的一一六个,老要会问:你有那么 重写过hashcode妙招 ?不少候选人直接说没写过。为啥让你 想,或许真的没写过,于是就再通过一一六个什么的问题确认:你在用HashMap的一一六个,键(Key)要素,有那么 放过自定义对象?而你你这个一一六个,候选人说放过,于是一一六个什么的问题的回答就自相矛盾了。

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

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

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

你们你们 再来观察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对象的内存地址(假设是11150)。

    

    一一六个你们你们 但是 是调用hm.get(k1),那么 你们你们 会再次调用hashCode妙招 (还是返回k1的地址11150),但是 根据得到的hash值,能加快速度地找到k1。

    但你们你们 这里的代码是hm.get(k2),当你们你们 调用Object类的hashCode妙招 (一一六个Key里没定义)计算k2的hash值时,嘴笨 得到的是k2的内存地址(假设是1150)。一一六个k1和k2是一一六个不同的对象,好多好多 它们的内存地址一定我不要 相同,也可是说它们的hash值一定不同,这可是你们你们 无法用k2的hash值去拿k1的是意味。

    当你们你们 把第16和17行的hashCode妙招 的注释加带后,会发现它是返回id属性的hashCode值,这里k1和k2的id也有1,好多好多 它们的hash值是相等的。

    你们你们 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是1150,把k1对象倒入到对应的位置。而取k2时,是先计算它的hash值(一一六个k2的id也是1,你你这个值也是1150),但是 到你你这个位置去找。

    但结果会出乎你们你们 意料:明明1150号位置一一六个有k1,但第26行的输出结果依然是null。其是意味可是那么 重写Key对象的equals妙招 。

    HashMap是用链地址法来防止冲突,也可是说,在1150号位置上,一一六个地处着多个用链表形式存储的对象。它们通过hashCode妙招 返回的hash值也有1150。

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