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 是两个“不同的地址”
public class TestAddress {
public static void main(String[] args) {
Address address1 = new Address("广东","广州");
Address address2 = new Address("广东", "广州");
System.out.println(address1 == address2);//false
System.out.println(address1.equals(address2));//false
System.out.println(address1.hashCode() == address2.hashCode());//false
}
}
其原因是:在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类而言,可以这样:
@Override
public 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);
}
第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;}
@Override
public 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);
}
@Override
public 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);//false
System.out.println(address1.equals(address2));//true
System.out.println(address1.hashCode() == address2.hashCode());//true Address diff1 = new Address("四川","成都");
Address diff2 = new Address("四川","绵阳");
System.out.println(diff1 == diff2);//false
System.out.println(diff1.equals(diff2));//false
System.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值1
System.out.println(hashMap.get(address1));//
System.out.println(hashMap.get(address2));// hashMap.put(diff1, 1);
hashMap.put(diff2, 2);
System.out.println(hashMap.get(diff1));//
System.out.println(hashMap.get(diff2));//
}
}
最后,其实Eclipse里面为我们提供了自动 生成 equals和hashCode的方法,可参考:JAVA中equals方法与hashCode方法学习。自动生成的方法如下:(还是自动生成的更专业呀。。。。)
@Override
public 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;
}
@Override
public 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
JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法的更多相关文章
- HashSet中存方用户自己定义数据类型数据,重写equals方法和hashCode方法
import java.util.Set; import java.util.HashSet; public class SetTest { public static void main(Strin ...
- 对象作为 map 的 key 时,需要重写 equals 方法和 hashCode 方法
对象作为 map 的 key 时,需要重写 hashCode 和 equals方法 如果没有重写 hashCode 方法,那么下面的代码示例会输出 null 我们首先定义一个对象:BmapPoint, ...
- Java重写equals方法和hashCode方法
package com.ddy; public class User { private Integer id; private String name; private St ...
- Hibernate中为什么要重写equals方法和hashcode方法
1.*为什么要重写equals方法,首先我们来看一下equals源码: public boolean equals(Object anObject) { if (this == anObject) { ...
- HashSet中的元素必须重写equals方法和hashCode方法
http://jingyan.baidu.com/article/d5a880eb8fb61d13f147cc99.html 1.为什么必须重写这两个方法. 2.什么事hashSet去重,符合什么样的 ...
- Java 如何重写对象的 equals 方法和 hashCode 方法
前言:Java 对象如果要比较是否相等,则需要重写 equals 方法,同时重写 hashCode 方法,而且 hashCode 方法里面使用质数 31.接下来看看各种为什么. 一.需求: 对比两个对 ...
- Java基础系列-equals方法和hashCode方法
原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述 equals方法和hashCode方法都是有Object类定义的. publi ...
- java中equals方法和hashcode方法的区别和联系,以及为什么要重写这两个方法,不重写会怎样
一.在Object类中的定义为:public native int hashCode();是一个本地方法,返回的对象的地址值.但是,同样的思路,在String等封装类中对此方法进行了重写.方法调用得到 ...
- 正确重写equals方法和compareTo方法
一.概述 程序要对一堆数据元素排序,查找,增加删除.数据节点 class Node{ int type; int index; int score; } 规则: 1)对象相等:两个节点n1与n2,如果 ...
随机推荐
- 栈长这里是生成了一个 Maven 示例项目。
Spring Cloud 的注册中心可以由 Eureka.Consul.Zookeeper.ETCD 等来实现,这里推荐使用 Spring Cloud Eureka 来实现注册中心,它基于 Netfl ...
- 【Loj117】有源汇上下界最小流(网络流)
[Loj117]有源汇上下界最小流(网络流) 题面 Loj 题解 还是模板题. #include<iostream> #include<cstdio> #include< ...
- [hgoi#2019/3/21]NOIP&NOI赛后总结
前言 今天做的是是2010年提高组和NOI的题目,做过几道原题,但是还是爆炸了,我真的太弱了. t1-乌龟棋 https://www.luogu.org/problemnew/show/P1541 这 ...
- 「SCOI2016」萌萌哒 解题报告
「SCOI2016」萌萌哒 这思路厉害啊.. 容易发现有个暴力是并查集 然后我想了半天线段树优化无果 然后正解是倍增优化并查集 有这个思路就简单了,就是开一个并查集代表每个开头\(i\)每个长\(2^ ...
- HDU6321 Dynamic Graph Matching (杭电多校3C)
给出一些点集,然后对于每一次要求给出的这些点集里的1,2,3,4,5,6....n/2的匹配数, dp[i][j] 表示到第i次操作里点集为j的匹配数,然后我每次加入一条边u-v,我的状态就是 dp[ ...
- sublime text3 replace和反向引用
实用小技巧,主要用于替换爬虫请求头,节省时间. chrome原信息显示: UserID: sds UserPass: sdsd codeKey: 350753 code: 277 B1: 提 subl ...
- poj 1144 (Tarjan求割点数量)
题目链接:http://poj.org/problem?id=1144 描述 一个电话线公司(简称TLC)正在建立一个新的电话线缆网络.他们连接了若干个地点分别从1到N编号.没有两个地点有相同的号码. ...
- haploview画出所有SNP的LD关系图
有时候我们想画出所有SNP的LD关系图,则需要在命令行添加“-skipcheck”命令行,如下所示: java -jar Haploview.jar -skipcheck -n -pedfile 80 ...
- (贪心 部分背包问题)悼念512汶川大地震遇难同胞——老人是真饿了 hdu2187
悼念512汶川大地震遇难同胞——老人是真饿了 http://acm.hdu.edu.cn/showproblem.php?pid=2187 Time Limit: 1000/1000 MS (Java ...
- IDEA常用快捷键整理(Mac OS X版本)
最近eclipse把我弄疯了!各种提示没有!烦,果断用了IDEA. 一.前提 IDEA版本:IntelliJ IDEA 15.0.3 Keymaps:Mac OS X 二.视图切换快捷键 1.cmd ...