大发快3官方网址_为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。

  • 时间:
  • 浏览:2
  • 来源:梦城博客 - 专注共享啊啊博客技术

     我在面试 Java初级开发的以后,老是 会问:你有越来越重写过hashcode法律方法?不少候选人直接说没写过。让我想,或许真的没写过,于是就再通过一一一三个白多多大问题确认:你在用HashMap的以后,键(Key)偏离 ,有越来越放过自定义对象?而你你什儿 以后,候选人说放过,于是一一一三个白多多大问题的回答就自相矛盾了。

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

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

    许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 先复习数据特性里的一一一三个白多多知识点:在一一一三个白多多长度为n(假设是60 00)的线性表(假设是ArrayList)里,存放着无序的数字;不可能 许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 要找一一一三个白多多指定的数字,就不得不通过从头到尾依次遍历来查找,本来的平均查找次数是n除以2(这里是60 00)。

许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 再来观察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对象的内存地址(假设是60 0)。

    

    不可能 许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 以后是调用hm.get(k1),越来越许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 会再次调用hashCode法律方法(还是返回k1的地址60 0),以后根据得到的hash值,能变慢地找到k1。

    但许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 这里的代码是hm.get(k2),许多人歌词 歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 调用Object类的hashCode法律方法(不可能 Key里没定义)计算k2的hash值时,着实 得到的是k2的内存地址(假设是60 0)。不可能 k1和k2是一一一三个白多多不同的对象,什么都它们的内存地址一定不需要相同,也本来说它们的hash值一定不同,这本来许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 无法用k2的hash值去拿k1的由于。

    许多人歌词 歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 把第16和17行的hashCode法律方法的注释去掉 后,会发现它是返回id属性的hashCode值,这里k1和k2的id都在1,什么都它们的hash值是相等的。

    许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 再来更正一下存k1和取k2的动作。存k1时,是根据它id的hash值,假设这里是60 ,把k1对象放在到对应的位置。而取k2时,是先计算它的hash值(不可能 k2的id也是1,你你什儿 值也是60 ),以后到你你什儿 位置去找。

    但结果会出乎许多人歌词 歌词 许多人歌词 歌词 许多人歌词 歌词 意料:明明60 号位置不可能 有k1,但第26行的输出结果依然是null。其由于本来越来越重写Key对象的equals法律方法。

    HashMap是用链地址法来补救冲突,也本来说,在60 号位置上,有不可能 发生着多个用链表形式存储的对象。它们通过hashCode法律方法返回的hash值都在60 。

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