极速1分快三邀请码_为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

  • 时间:
  • 浏览:1
  • 来源:北京新闻网_北京主流媒体_北京门户网站

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

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

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

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

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

    

    不可能 亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 但是 是调用hm.get(k1),这麼 亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 会再次调用hashCode方法 (还是返回k1的地址2000),但是 根据得到的hash值,能快一点 地找到k1。

    但亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 这里的代码是hm.get(k2),当亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 调用Object类的hashCode方法 (不可能 Key里没定义)计算k2的hash值时,嘴笨 得到的是k2的内存地址(假设是2000)。不可能 k1和k2是一八个 多不同的对象,什么都 它们的内存地址一定不用相同,也就是说它们的hash值一定不同,这就是亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 无法用k2的hash值去拿k1的是因为分析。

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

    亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是200,把k1对象贴到 到对应的位置。而取k2时,是先计算它的hash值(不可能 k2的id也是1,你这人值也是200),但是 到你这人位置去找。

    但结果会出乎亲戚亲戚让让我们 歌词 让让我们 歌词 让让我们 歌词 让让我们 歌词 意料:明明200号位置不可能 有k1,但第26行的输出结果依然是null。其是因为分析就是这麼 重写Key对象的equals方法 。

    HashMap是用链地址法来处理冲突,也就是说,在200号位置上,有不可能 指在着多个用链表形式存储的对象。它们通过hashCode方法 返回的hash值都在 200。

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