在实际应用中经常会比较两个对象是否相等,比如下面的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方法相关推荐

  1. HashSet要重写equals方法和hashCode方法

    注:重要笔记在代码中注释有 hashSet去重: 即判断两个对象是否相等 1:会先调用对象的hashCode方法获得hash的值,如果set中哈希表里面没有对应的hash值,则将次对象存入set中 2 ...

  2. java重写6,java重写equals()方法和hashCode()方法

    1.equals()方法和hashCode()方法是什么? equals()和hashCode()都是是Java中万物之源Object类中的方法: equals方法用于比较两个对象是否相同,Objec ...

  3. java中重写hashcode_Java中HashSet要重写equals方法和hashCode方法

    下面给出一个属性不同但哈希码相同的例子: import java.util.HashSet; import java.util.Iterator; import java.util.Set; clas ...

  4. Java基础语法:重写equals方法和hasCode方法

    目录 一.为什么需要重写equals和hasCode方法 二.equals和hasCode解读 1.equals方法 2.hasCode方法 三.重写 四.参考资料 一.为什么需要重写equals和h ...

  5. 为什么要重写toString()方法和hashcode()方法

    一.toString(): 在Object类里面定义toString()方法的时候返回的对象的哈希code码,这个hashcode码不能简单明了的表示出对象的属性.所以要重写toString()方法. ...

  6. Java equals()方法和hashCode()方法

    equals()方法 如果满足了以下任何一个条件,就不需要覆盖equals()方法: 1 类的每个实例本质上都是唯一的. 2 不关心类是否提供了"逻辑相等"的测试功能. 3 父类已 ...

  7. 普歌-码灵团队-java中的equals方法和toString方法及总结

    普歌-码灵团队:Object类中的方法介绍及其用法 一.Object类 二.方法介绍 1.toString方法 2.重写equals方法: 3. 拓展 三.总结 一.Object类的简单介绍 二.Ob ...

  8. java实现Comparable接口和Comparator接口,并重写compareTo方法和compare方法

    原文地址https://segmentfault.com/a/1190000005738975 实体类:java.lang.Comparable(接口) + comareTo(重写方法),业务排序类 ...

  9. 实现Comparable接口和Comparator接口,并重写compareTo方法和compare方法

    实体类:java.lang.Comparable(接口) + comareTo(重写方法),业务排序类 java.util.Comparator(接口) + compare(重写方法). 这两个接口我 ...

最新文章

  1. CCS MAP文件说明
  2. 汇编语言-环境安装及各个寄存器介绍
  3. [译]使用JavaScript来操纵数据视图DataView新建视图的默认值
  4. Xargs用法详解(原创)
  5. 稳定排序与不稳定排序的定义
  6. AJAX——注册新用户的重名提示
  7. c语言中图形驱动程序功能_C / C ++中的图形:一些更有趣的功能
  8. php://filter利用条件,浅谈php://filter技巧
  9. 【珍藏版】长文详解python正则表达式
  10. OpenCV-Python图片叠加与融合,cv2.add与cv2.addWeighted的区别
  11. linux memery dump
  12. 如何制定 - 测试计划和策略 (详细讲解)
  13. 学生托管班_小学生托管班托管班价格是多少?如何收费?
  14. s3cmd 快速评估RADOSGW的性能
  15. 手动btsoft_ubuntu
  16. OO ALV简单报表之DOCKING容器实现
  17. 沈丘县司法局法治进校园金秋第一堂法治课
  18. iPhone4s刷机教程
  19. 淘宝/天猫获得淘宝店铺详情 API
  20. 【Introduction to Artificial Intelligence and Data Analytics】(TBC)

热门文章

  1. c语言一串字符括号配对,C语言实现括号匹配的方法
  2. Android JNI学习(五)——Java与Native之间如何实现相互调用
  3. ethtool的内核流程跟踪
  4. 【kafka】kafka kerberos Cannot locate KDC Unable to locate KDC for realm
  5. 【kafka】kafka kerberos KeeperErrorCode = InvalidACL for /config/topics
  6. 【kafka】 kafka如何设置指定分区进行发送和消费
  7. 【Elasticsearch】es 查询 multi_match 与 match_phrase
  8. 【ElasticSearch】Es 源码之 Discovery DiscoveryModule Coordinator 源码解读
  9. 【Flink】TableException: A raw type backed by type information has no serializable
  10. 30-10-010-编译-kylin-on-druid-2.6.0-CDH57编译