JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法
在实际应用中经常会比较两个对象是否相等,比如下面的Address类,它有两个属性:String province 和 String city。
public class Address {private String province;private String city;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public Address() {}public Address(String province, String city) {this.province = province; this.city = city;}}
在现实生活中我们认为若两个 Address 的 province 和 city 属性相同,则它们应该是同一个地址(省市都一样,当然就是同一个地区啦)。但下面的代码却表明:address1 和 address2 是两个“不同的地址”
1 public class TestAddress { 2 3 public static void main(String[] args) { 4 Address address1 = new Address("广东","广州"); 5 Address address2 = new Address("广东", "广州"); 6 7 System.out.println(address1 == address2);//false 8 System.out.println(address1.equals(address2));//false 9 System.out.println(address1.hashCode() == address2.hashCode());//false 10 } 11 }
其原因是:在JAVA(编程语言)中比较对象 和 现实生活中 比较两个对象是两个不同的概念,前者暂且称为“物理相等”,而后者是“逻辑相等”。
adress1==adress2 是根据两个对象的内存地址是否相同进行比较的,第4、5行分别 new 了两个对象,这两个对象存储在内存中不同的地址,当然不会相等了。
由于Address类并没有重写equals方法,那么address1.equals(address2) 执行的就是Object类的equals方法,而java.lang.Object类的equlas方法是这样实现的:
public boolean equals(Object obj) {return (this == obj);}
JDK中对该方法的注释如下:也就是说:Object类的equals方法 是通过 == 来比较两个对象是否相等的,也即根据 对象x引用 和 对象y 的引用是否指向内存中的同一个地址 来判断 对象x 和 对象y 是否相等。
* The {@code equals} method for class {@code Object} implements* the most discriminating possible equivalence relation on objects;* that is, for any non-null reference values {@code x} and* {@code y}, this method returns {@code true} if and only* if {@code x} and {@code y} refer to the same object* ({@code x == y} has the value {@code true}).
而按照现实思维,既然 adress1 和 address2 都代表广东广州,那么在程序中它们两个对象比较就应该相等(逻辑上相等),也即address1.equals(address2)应该返回true才对。于是就需要覆盖 Object 类中的 equals 方法 和 hashCode方法了。
而覆盖 equals方法 和hashCode方法是需要技巧的。
①覆盖了Object类的equals方法后,需要再覆盖 Object类的hashCode方法。为什么呢?----不要违反“hashCode方法的 通过约定(general contract) ”
Object类的equals方法上的注释如下:
/ * Note that it is generally necessary to override the {@code hashCode}* method whenever this method is overridden, so as to maintain the* general contract for the {@code hashCode} method, which states* that equal objects must have equal hash codes.*/public boolean equals(Object obj) {return (this == obj);}
而这个“通用约定”就是:❶若两个对象根据equals(Object)方法比较相等,那么调用这两个对象中任意一个对象的hashCode方法 都必须 产生同样的整数结果。
❷若两个对象根据equals(Object)方法比较不相等,那么调用这两个对象中任意一个对象的hashCode方法 可以 产生相同的整数结果,但是最好 产生不同的整数结果,这样可以提供散列表的性能(当要把Address类 作为 键 put 到HashMap中时,可以减少冲突,可参考这篇文章)
那具体如何正确地覆盖equals()呢?《Effective JAVA》里面给出了方法,套路是一样的,其目标是保证:自反性、对称性、一致性。总之,对于上面的Address类而言,可以这样:
1 @Override 2 public boolean equals(Object obj) { 3 if(obj == this) 4 return true; 5 if(!(obj instanceof Address)) 6 return false; 7 Address address = (Address)obj; 8 return address.getProvince().equals(province) && address.getCity().equals(city); 9 }
第8行从表明如果两个Address的 province 和 city 相同,那这两个Address就是相同的,这样equals方法就会返回true了。(不考虑字符串大小写问题)
覆盖完了equals(),接下来就是 覆盖hashCode()了。覆盖hashCode()的目标是:
如果两个对象 address1.equals(address2) 返回 false,那么 address1.hashCode() 最好 不等于 address2.hashCode()
当然,没有超级完美的hashCode(),如果相等了,那么当 hashMap.put(address1,value1) hashMap.put(address2,value2) 就会put到同一个 hashmap的同一个槽下了。
重写了equals和hashCode的Address类如下:
public class Address {private String province;private String city;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public Address() {}public Address(String province, String city) {this.province = province; this.city = city;}@Overridepublic boolean equals(Object obj) {if(obj == this)return true;if(!(obj instanceof Address))return false;Address address = (Address)obj;return address.getProvince().equals(province) && address.getCity().equals(city);}@Overridepublic int hashCode() {int result = 17;result += 31 * province.hashCode();result += 31 * city.hashCode();return result;} }
测试类如下:
import java.util.HashMap; import java.util.Map;public class TestAddress {public static void main(String[] args) {Address address1 = new Address("广东","广州");Address address2 = new Address("广东", "广州");System.out.println(address1 == address2);//falseSystem.out.println(address1.equals(address2));//trueSystem.out.println(address1.hashCode() == address2.hashCode());//true Address diff1 = new Address("四川","成都");Address diff2 = new Address("四川","绵阳");System.out.println(diff1 == diff2);//falseSystem.out.println(diff1.equals(diff2));//falseSystem.out.println(diff1.hashCode() == diff2.hashCode());//false Map<Address, Integer> hashMap = new HashMap<Address, Integer>();hashMap.put(address1, 1);hashMap.put(address2, 2);//address2的hashCode 和 address1 相同,因此 put 方法会覆盖 address1 对应的 Value值1System.out.println(hashMap.get(address1));//2System.out.println(hashMap.get(address2));//2 hashMap.put(diff1, 1);hashMap.put(diff2, 2);System.out.println(hashMap.get(diff1));//1System.out.println(hashMap.get(diff2));//2 } }
最后,其实Eclipse里面为我们提供了自动 生成 equals和hashCode的方法,可参考:JAVA中equals方法与hashCode方法学习。自动生成的方法如下:(还是自动生成的更专业呀。。。。)
@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((city == null) ? 0 : city.hashCode());result = prime * result + ((province == null) ? 0 : province.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Address other = (Address) obj;if (city == null) {if (other.city != null)return false;} else if (!city.equals(other.city))return false;if (province == null) {if (other.province != null)return false;} else if (!province.equals(other.province))return false;return true;}
参考:《effective java》
http://www.cnblogs.com/hapjin/p/4582795.html
原文:http://www.cnblogs.com/hapjin/p/7327839.html
转载于:https://www.cnblogs.com/hapjin/p/7327839.html
JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法相关推荐
- HashSet要重写equals方法和hashCode方法
注:重要笔记在代码中注释有 hashSet去重: 即判断两个对象是否相等 1:会先调用对象的hashCode方法获得hash的值,如果set中哈希表里面没有对应的hash值,则将次对象存入set中 2 ...
- java重写6,java重写equals()方法和hashCode()方法
1.equals()方法和hashCode()方法是什么? equals()和hashCode()都是是Java中万物之源Object类中的方法: equals方法用于比较两个对象是否相同,Objec ...
- java中重写hashcode_Java中HashSet要重写equals方法和hashCode方法
下面给出一个属性不同但哈希码相同的例子: import java.util.HashSet; import java.util.Iterator; import java.util.Set; clas ...
- Java基础语法:重写equals方法和hasCode方法
目录 一.为什么需要重写equals和hasCode方法 二.equals和hasCode解读 1.equals方法 2.hasCode方法 三.重写 四.参考资料 一.为什么需要重写equals和h ...
- 为什么要重写toString()方法和hashcode()方法
一.toString(): 在Object类里面定义toString()方法的时候返回的对象的哈希code码,这个hashcode码不能简单明了的表示出对象的属性.所以要重写toString()方法. ...
- Java equals()方法和hashCode()方法
equals()方法 如果满足了以下任何一个条件,就不需要覆盖equals()方法: 1 类的每个实例本质上都是唯一的. 2 不关心类是否提供了"逻辑相等"的测试功能. 3 父类已 ...
- 普歌-码灵团队-java中的equals方法和toString方法及总结
普歌-码灵团队:Object类中的方法介绍及其用法 一.Object类 二.方法介绍 1.toString方法 2.重写equals方法: 3. 拓展 三.总结 一.Object类的简单介绍 二.Ob ...
- java实现Comparable接口和Comparator接口,并重写compareTo方法和compare方法
原文地址https://segmentfault.com/a/1190000005738975 实体类:java.lang.Comparable(接口) + comareTo(重写方法),业务排序类 ...
- 实现Comparable接口和Comparator接口,并重写compareTo方法和compare方法
实体类:java.lang.Comparable(接口) + comareTo(重写方法),业务排序类 java.util.Comparator(接口) + compare(重写方法). 这两个接口我 ...
最新文章
- CCS MAP文件说明
- 汇编语言-环境安装及各个寄存器介绍
- [译]使用JavaScript来操纵数据视图DataView新建视图的默认值
- Xargs用法详解(原创)
- 稳定排序与不稳定排序的定义
- AJAX——注册新用户的重名提示
- c语言中图形驱动程序功能_C / C ++中的图形:一些更有趣的功能
- php://filter利用条件,浅谈php://filter技巧
- 【珍藏版】长文详解python正则表达式
- OpenCV-Python图片叠加与融合,cv2.add与cv2.addWeighted的区别
- linux memery dump
- 如何制定 - 测试计划和策略 (详细讲解)
- 学生托管班_小学生托管班托管班价格是多少?如何收费?
- s3cmd 快速评估RADOSGW的性能
- 手动btsoft_ubuntu
- OO ALV简单报表之DOCKING容器实现
- 沈丘县司法局法治进校园金秋第一堂法治课
- iPhone4s刷机教程
- 淘宝/天猫获得淘宝店铺详情 API
- 【Introduction to Artificial Intelligence and Data Analytics】(TBC)
热门文章
- c语言一串字符括号配对,C语言实现括号匹配的方法
- Android JNI学习(五)——Java与Native之间如何实现相互调用
- ethtool的内核流程跟踪
- 【kafka】kafka kerberos Cannot locate KDC Unable to locate KDC for realm
- 【kafka】kafka kerberos KeeperErrorCode = InvalidACL for /config/topics
- 【kafka】 kafka如何设置指定分区进行发送和消费
- 【Elasticsearch】es 查询 multi_match 与 match_phrase
- 【ElasticSearch】Es 源码之 Discovery DiscoveryModule Coordinator 源码解读
- 【Flink】TableException: A raw type backed by type information has no serializable
- 30-10-010-编译-kylin-on-druid-2.6.0-CDH57编译