热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么覆盖equals方法一定要覆盖hashCode方法

1.Java中Equals和的区别:(没区别,都是比较物理地址,但是equals可以被覆盖)首先equals和最大的区别是一个是方法一个是运算符,在Java中,二者

1.  Java 中 Equals和==的区别: (没区别,都是比较物理地址,但是equals可以被覆盖)

首先equals和== 最大的区别是一个是方法一个是运算符,在Java中,二者比较的都是物理地址 而不是值得比较。

我们举个例子这样比较直观。

Student student1 = new Student();

Student student2 = new Student();

System.out.println(student1.equals(student2));

System.out.println(student1 == student2);

不论是用的是哪一种方法 , 最终的结果显示的都是false,大家不妨可以试一下。为什么呢?就是因为他们比较的不是对象中字段的值或者说本身对象的值,而比较的是物理地址。

我们再来举一个例子。

String a = new String("a");

String b = new String("a");

System.out.println(a == b);

System.out.println(a.equals(b));

当我们创建2个String对象是 我们会发现 执行的结果是 false true。为什么这次euqals返回的值编程了true?因为此时equals方法不单单是比较物理地址 同时也比较了值,

在String中 equals方法被重写 当物理地址不同时,会进一步比较值


2. HashMap param = new HashMap();   当存数据比较key时,不能直接比较两个key对象,因为LoanInfo都是继承了Object的equals和hashcode方法,但他们都是比较的物理地址,所以LoanInfo作为key时比较,就需要覆盖LoanInfo对象中的equals方法和hashcode方法,equals方法中就是用户觉得哪些字段相等时就可以算相等,hashcode方法里面主要是自己取LoanInfo中的一些比较属性的hash值。


上面懂了下面都可以不用看:

两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? 
两个对象有相同的hash code,但是值不同(x.equals(y) == false),这句话对不对? 
对,hash code相同,说明value在同一个bucket里面,也可以说是hash冲突,x.equals(y)为true说明x和y是同一个业务对象,x.equals(y)为false说明不是同一个业务对象,产生了hash冲突。所以如果x.equals(y)==false,程序员应该知道,给不相同的x和y对象产生截然不同的hash code,有可能提高散列表(hash table)的性能。 
因为如果只覆盖了equals而没有覆盖hashCode, 则两个不同的instance a和b虽然equals结果(业务逻辑上)相等,但却会有不同的hashcode,这样hashmap里面会同时存在a和b,而实际上我们需要hashmap里面只能保存其中一个,因为从业务逻辑方向看它们是相等的. 

为了能让集合框架中的类如HashMap正常工作,必须保证同时覆盖equals()和hashCode(),而且注意不要由于写错了参数类型,而重载了这两个方法,却并没有覆盖它们,比如: 
public boolean equals(Object obj) 写成了public boolean equals(ClassXXX obj) 。 

为什么在覆盖equals时一定也要覆盖hashCode呢? 下面用HashMap来阐述原因,首先假设key1和key2的值在业务逻辑领域是相等的,即它们应该是同一个对象,HashMap已经存储了key1,现在要查找key2是否存在,正确的结果应该是存在: 
Java中的HashMap实际上是一个链表数组,即首先HashMap是一个数组,然后数组中的每一个元素是一个链表(更通用的概念可以称为桶bucket,Java中的HashMap用Entry类描述链表的结点结构),HashMap在执行Put,Contains之类的操作时,会首先根据你提供的Key计算hashCode值,然后根据这个hashCode值在数组中找到某一个链表或桶(通常是找到链表的起始结点),这一步操作利用了hashCode()方法,如果你覆盖了就会用你提供的方法,在找到某一个链表的起始结点后,就会遍历链表,然后通过equals方法来寻找是否存在与Key的值相等的结点,如果执行equals方法后的结果相等,HashMap就认为已经存在这个元素,这一步如果你覆盖了equals方法就会用到你提供的equals方法。 
通过上面的描述,我们发现equals方法和hashCode方法如果不同时按你自己逻辑覆盖的话,HashMap就会出问题。比如你只覆盖了equals方法而没有覆盖hashCode方法,那么HashMap在第一步寻找链表的时候会出错,有同样值的两个对象Key1和Key2并不会指向同一个链表或桶,因为你没有提供自己的hashCode方法,那么就会使用Object的hashCode方法,该方法是根据内存地址来比较两个对象是否一致,由于Key1和Key2有不桶的内存地址,所以会指向不同的链表,这样HashMap会认为key2不存在,虽然我们期望Key1和Key2是同一个对象;反之如果只覆盖了hashCode方法而没有覆盖equals方法,那么虽然第一步操作会使Key1和Key2找到同一个链表,但是由于equals没有覆盖,那么在遍历链表的元素时,key1.equals(key2)也会失败(事实上Object的equals方法也是比较内存地址),从而HashMap认为不存在Key2对象,这同样也是不正确的。 

以下内容摘自<
覆盖equals时总要覆盖hashCode,一个很常见的错误根源在没有覆盖hashCode方法。在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常工作,这样的集合包括HashMap、HashSet和Hashtable。 

下面是约定的内容,摘自Object规范[JavaSE6]: 
1)在应用程序的执行期间,只要对象的equals方法所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。 

2)如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。 

3)如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中的任意一个对象的hashCode方法,则不一定要产生不同的整数结果。但是程序员应该知道,给不相同的对象产生截然不同的整数结果,有可能提高散列表(hash table)的性能。 

如果覆盖equals没有覆盖hashCode,将会违反上面的第2条:相等的对象必须具有相等的散列码(hashCode)。Object类里面的默认eqals方法是比较内存地址是否相等,默认的hashCode方法则是根据内存地址产生一个整数,所以Object类本身当然是符合上面规则的。当你覆盖了equals后,内存地址不同的对象可能会相等,而如果这时你没有覆盖hashCode方法的话,hashCode还是根据内存地址来生成,就会出现相等的对象具有不同的散列码的情况。 

下面给出了一个重载equals和hashCode的样本:
 

Java代码   收藏代码
  1. package core;  
  2.   
  3. public class OverrideEqualsAndHashCodeSample {  
  4.     private String name;  
  5.     private String region;  
  6.     private String position;  
  7.          
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.   
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.   
  16.     public String getRegion() {  
  17.         return region;  
  18.     }  
  19.   
  20.     public void setRegion(String region) {  
  21.         this.region = region;  
  22.     }  
  23.   
  24.     public String getPosition() {  
  25.         return position;  
  26.     }  
  27.   
  28.     public void setPosition(String position) {  
  29.         this.position = position;  
  30.     }  
  31.   
  32.     @Override  
  33.     public boolean equals(Object o) {  
  34.         if(o==nullreturn false;  
  35.         if(!(o instanceof OverrideEqualsAndHashCodeSample)) return false;  
  36.         if(this==o) return true;  
  37.         OverrideEqualsAndHashCodeSample o2=(OverrideEqualsAndHashCodeSample)o;  
  38.         if(name.equalsIgnoreCase(o2.name)  
  39.             && region.equalsIgnoreCase(o2.region)  
  40.             && position.equalsIgnoreCase(o2.position))  
  41.             return true;  
  42.         return false;  
  43.     }  
  44.       
  45.     @Override  
  46.     public int hashCode() {  
  47.       int result=17;  
  48.       result=31*result+name!=null?name.hashCode():0;  
  49.       result=31*result+region!=null?region.hashCode():0;  
  50.       result=31*result+position!=null?position.hashCode():0;  
  51.       return result;  
  52.     }  
  53. }  




推荐阅读
author-avatar
留心6_136
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有