Java 中正确使用 hashCode 和 equals 方法
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解。我将讨论他们的默认实现,以及如何正确的重写他们。我也将使用Apache Commons提供的工具包做一个实现。
目录:
hashCode()和equals()的用法
重写默认实现
使用Apache Commons Lang包重写hashCode()和equals()
需要注意记住的事情
当使用ORM的时候特别要注意的
hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。
使用hashCode()和equals()
hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。
重写默认的实现
如果你不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。
来看看这个例子,让我们创建一个简单的类Employee
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public class Employee{ private Integer id; private String firstname; private String lastName; private String department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; }} |
上面的Employee类只是有一些非常基础的属性和getter、setter.现在来考虑一个你需要比较两个employee的情形。
?
|
1
2
3
4
5
6
7
8
9
10
11
|
public class EqualsTest { public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints false in console System.out.println(e1.equals(e2)); }} |
毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个employee。真正的商业逻辑希望我们返回true。
为了达到这个目的,我们需要重写equals方法。
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public boolean equals(Object o) { if(o == null) { return false; } if (o == this) { return true; } if (getClass() != o.getClass()) { return false; } Employee e = (Employee) o; return (this.getId() == e.getId());} |
在上面的类中添加这个方法,EauqlsTest将会输出true。
So are we done?没有,让我们换一种测试方法来看看。
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.util.HashSet;import java.util.Set;public class EqualsTest{ public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints 'true' System.out.println(e1.equals(e2)); Set<Employee> employees = new HashSet<Employee>(); employees.add(e1); employees.add(e2); //Prints two objects System.out.println(employees); } |
上面的程序输出的结果是两个。如果两个employee对象equals返回true,Set中应该只存储一个对象才对,问题在哪里呢?
我们忘掉了第二个重要的方法hashCode()。就像JDK的Javadoc中所说的一样,如果重写equals()方法必须要重写hashCode()方法。我们加上下面这个方法,程序将执行正确。
?
|
1
2
3
4
5
6
7
8
|
@Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + getId(); return result; } |
使用Apache Commons Lang包重写hashCode() 和equals()方法
Apache Commons 包提供了两个非常优秀的类来生成hashCode()和equals()方法。看下面的程序。
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
import org.apache.commons.lang3.builder.EqualsBuilder;import org.apache.commons.lang3.builder.HashCodeBuilder;public class Employee{ private Integer id; private String firstname; private String lastName; private String department;public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; }@Override public int hashCode() { final int PRIME = 31; return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME). toHashCode(); }@Override public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (o.getClass() != getClass()) return false; Employee e = (Employee) o; return new EqualsBuilder(). append(getId(), e.getId()). isEquals(); } } |
如果你使用Eclipse或者其他的IDE,IDE也可能会提供生成良好的hashCode()方法和equals()方法。
需要注意记住的事情
尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。在我们的案例中,我们使用员工id。
eqauls方法必须保证一致(如果对象没有被修改,equals应该返回相同的值)
任何时候只要a.equals(b),那么a.hashCode()必须和b.hashCode()相等。
两者必须同时重写。
当使用ORM的时候特别要注意的
如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量。因为在ORM中有的时候成员变量会被延时加载,这些变量只有当getter方法被调用的时候才真正可用。
例如在我们的例子中,如果我们使用e1.id == e2.id则可能会出现这个问题,但是我们使用e1.getId() == e2.getId()就不会出现这个问题。
希望这篇文章能够帮助你。
Java 中正确使用 hashCode 和 equals 方法的更多相关文章
- (转)Java 中正确使用 hashCode 和 equals 方法
背景:最近在编写持久化对象时候遇到重写equals和hashCode方法的情况,对这两个方法的重写做一个总结. 链接:https://www.oschina.net/question/82993_75 ...
- Java中正确使用hashCode和equals方法
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...
- Java中正确使用hashCode() 和equals() 、==
在java编程中,经常会遇到两个对象中某个属性的比较,常常会用到的方法有: == .equals().但是两者使用起来有什么区别呢? 一.== java中的==是比较两个对象在JVM中的地址.比较好理 ...
- 重写Java Object对象的hashCode和equals方法实现集合元素按内容判重
Java API提供的集合框架中Set接口下的集合对象默认是不能存储重复对象的,这里的重复判定是按照对象实例句柄的地址来判定的,地址相同则判定为重复,地址不同不管内容如何都判定为不重复,这有时与需求不 ...
- java为什么要重写hashCode和equals方法?
如果不被重写(原生)的hashCode和equals是什么样的? 不被重写(原生)的hashCode值是根据内存地址换算出来的一个值. 不被重写(原生)的equals方法是严格判断一个对象是否相等的方 ...
- Java中编写一个完美的equals方法
首先看下Java语言规范对equals方法的要求: 1,自反性,对于任何非控引用x,x.equals(x)都应该返回true. 2,对称性,对于任何引用x和y,如果x.equals(y)返回true, ...
- 【java编程】重写HashCode和equals方法
[一]重写equals方案的规则 equals方法本来的原则 1.类的每个实例本质上都是唯一的. 2.不关心类是否提供了“逻辑相等”的测试功能 3.超类已经覆盖了equals,从超类继承过来的行为对于 ...
- K:java中的hashCode和equals方法
hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals.通过翻看Object类 ...
- 关于java中的hashcode和equals方法原理
关于java中的hashcode和equals方法原理 1.介绍 java编程思想和很多资料都会对自定义javabean要求必须重写hashcode和equals方法,但并没有清晰给出为何重写此两个方 ...
随机推荐
- CSS3 动态魔方的展示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- OC中-数组是如何遍历的?
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSAutoreleasePool ...
- 原生JQ实现图片滑动轮播
<head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8&quo ...
- arclist底层模板字段,可以调用的字段列表
arclist底层模板字段,可以调用的字段列表 用DedeCMS做站,arclist是用得最多的标签,因为他是调用文章的基本标签,功能也非常强大,他的底层字段比较多,我们平时使用还没有用到一半,但 ...
- Android——获取网络图片
布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:too ...
- TortoiseSVN 安装中文语言包,SVN中文语言包
SVN中TortoiseSVN 是比较出门的一款SVN软件 TortoiseSVN 是Subversion 版本控制系统的一个免费开源客户端. 由于TortoiseSVN 默认是英文的:所以很多小伙伴 ...
- Dr.com校园网客户端故障解决方法
一,登录客户端的时候提示“登录超时失败” 解决办法:单击桌面右下角的联网图标然后右键,打开网络和共享中心,在左侧导航栏中找到更改网络适配器,找到本地连接(或者无线连接),点击右键找到详细信息,看看ip ...
- Java Thread interrupt
现有线程对象threadA,调用threadA.interrupt(),则threadA中interrupted状态会被置成false,很多线程中都是通过isInterrupted()方法来检测线程是 ...
- 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记2 Xcode、Auto Layout及MVC
原文链接不知道在哪, 接着上一话来讲,上一话中讲到了MVC,那么MVC在IOS8开发中是如何应用的呢?Paul Hegarty老师给我们展示了一个计算器的Demo,首先新建一个工程,老师把AppDel ...
- TCP/IP 教程
TCP/IP 是因特网的通信协议. 通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信. 浏览器和服务器都在使用 TCP/IP 因特网浏览器和因特网服务器均使用 TCP/ ...