码迷,mamicode.com
首页 > 其他好文 > 详细

关于equals和hashcode方法

时间:2015-04-25 11:59:55      阅读:141      评论:0      收藏:0      [点我收藏+]

标签:

首先声明一下,这篇文章仅仅讨论引用类型

所有引用类型都是继承自Object类,Object类有两个重要的方法:equals(),hashCode()经常被使用,虽然表面上你可能看不到你的代码里使用hashcode()。

Object类是这样实现equals方法的:

1   public boolean equals(Object obj) {
2     return (this == obj);
3     }

可见默认情况下==和equals()方法是一样的,即比较两个对象的地址。

从技术上来说,只要是通过new创建出来的实例,任何情况下equals和==都是false;

从业务上来说,有的时候我们希望即使不是同一个实例也可以相等:比如在系统中的不同组件之间,可以传递具有相同身份证信息的人的不同实例,但是在各个组件看来,这些不同实例对应的应该是同一个人,因为他们具有相同的身份证信息!

所以在开发过程中就产生了==不相等但是需要equals相等的矛盾!(因为==我们无法改变,所以业务逻辑只能寄希望于改变equals的实现)。

以身份证为某个人唯一标示作为例子,我们定义的时候应该是这样的:

技术分享
 1 /**
 2  * 具有相同身份证信息的人就是同一个人
 3  * 
 4  * @author Lucifer
 5  * 
 6  */
 7 public class Person {
 8 
 9     private String name;
10     private String identityCard;
11 
12     public Person(String name, String identityCard) {
13         this.name = name;
14         this.setIdentityCard(identityCard);
15     }
16 
17     public String getName() {
18         return name;
19     }
20 
21     public void setName(String name) {
22         this.name = name;
23     }
24 
25     public String getIdentityCard() {
26         return identityCard;
27     }
28 
29     public void setIdentityCard(String identityCard) {
30         this.identityCard = identityCard;
31     }
32     
33     @Override
34     public boolean equals(Object obj) {
35         if (obj == null) {
36             return false;
37         }
38         if (!(obj instanceof Person)) {
39             return false;
40         }
41         Person person = (Person) obj;
42 
43         return person.getIdentityCard().equals(this.identityCard);
44 
45     }
46 }
Person类

说到这里终于可以进入正题了,因为我们有了需要改变equals规则的需求。Object的hashcode有一些规约:

Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.

The general contract of hashCode is:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the java.lang.Object.equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)

红色字体部分经常被面试官问道,所以不管你想不想真正理解,你都应该记住:

1.如果两个object通过equals方法判断为相等,那么hashcode方法必须返回相同的整型值。

2.如果两个object通过equals方法判断为不相等,那么hashcode方法不要求必须返回不相等的整型值。但是程序员应该为不相等(通过equals方法判断的)的object返回不同的hashcode,以提高hashtables的性能。

这是我们就想到这个跟HashTable有什么关系呢,虽然没有强制不相等的时候重写hashCode方法,但是建议我们重写以提高HashTable的性能。

这里就涉及到了HashTable的实现,很多方法都用到了object的hashCode方法,以HashTable的get方法为例:

技术分享
 1    public synchronized V get(Object key) {
 2         int hash = Collections.secondaryHash(key);
 3         HashtableEntry<K, V>[] tab = table;
 4         for (HashtableEntry<K, V> e = tab[hash & (tab.length - 1)];
 5                 e != null; e = e.next) {
 6             K eKey = e.key;
 7             if (eKey == key || (e.hash == hash && key.equals(eKey))) {
 8                 return e.value;
 9             }
10         }
11         return null;
12     }
View Code

第七行代码:if (eKey == key || (e.hash == hash && key.equals(eKey)))如果红色部分可以判断出两个对象不相等,那么就没必要执行蓝色部分的代码了。而且如果业务上重写equals方法,可能涉及到很多代码,效率降低。

另外还有一个场景:对于实现Set接口的类,要求不能有重复的对象,在调用add方法的时候,通过hashCode判断明明equals方法相等的对象不存在,这是不是也是可能和业务逻辑上有矛盾呢!

这就是为什么如果两个object调用equals不相等,但是规范中建议重写hashCode方法的原因。

那么如何重写hashCode呢?

最简单的方法:如果你使用Eclipse开发,那么在需要重写hashCode和equals的类中右键选择source->Generate hashcode and equals

上面我们的例子用这个方法生成的代码如下:

技术分享
 1 /**
 2  * 具有相同身份证信息的人就是同一个人
 3  * 
 4  * @author Lucifer
 5  * 
 6  */
 7 public class Person {
 8 
 9     private String name;
10     private String identityCard;
11 
12     public Person(String name, String identityCard) {
13         this.name = name;
14         this.setIdentityCard(identityCard);
15     }
16 
17     public String getName() {
18         return name;
19     }
20 
21     public void setName(String name) {
22         this.name = name;
23     }
24 
25     public String getIdentityCard() {
26         return identityCard;
27     }
28 
29     public void setIdentityCard(String identityCard) {
30         this.identityCard = identityCard;
31     }
32     
33     @Override
34     public int hashCode() {
35         final int prime = 31;
36         int result = 1;
37         result = prime * result
38                 + ((identityCard == null) ? 0 : identityCard.hashCode());
39         result = prime * result + ((name == null) ? 0 : name.hashCode());
40         return result;
41     }
42 
43     @Override
44     public boolean equals(Object obj) {
45         if (this == obj)
46             return true;
47         if (obj == null)
48             return false;
49         if (getClass() != obj.getClass())
50             return false;
51         Person other = (Person) obj;
52         if (identityCard == null) {
53             if (other.identityCard != null)
54                 return false;
55         } else if (!identityCard.equals(other.identityCard))
56             return false;
57         if (name == null) {
58             if (other.name != null)
59                 return false;
60         } else if (!name.equals(other.name))
61             return false;
62         return true;
63     }
64 }
View Code

如果加上一个int类型的字段age,生成的代码如下:

 1 @Override
 2     public int hashCode() {
 3         final int prime = 31;
 4         int result = 1;
 5         result = prime * result + age;
 6         result = prime * result
 7                 + ((identityCard == null) ? 0 : identityCard.hashCode());
 8         result = prime * result + ((name == null) ? 0 : name.hashCode());
 9         return result;
10     }

因为对于基本类型他们的值就是他们的HashCode。

当然,我们还注意到hashCode方法返回的是int类型。那按以上的实现方式会不会出现问题呢,比如我们把age改成long:

 1 @Override
 2     public int hashCode() {
 3         final int prime = 31;
 4         int result = 1;
 5         result = prime * result + (int) (age ^ (age >>> 32));
 6         result = prime * result
 7                 + ((identityCard == null) ? 0 : identityCard.hashCode());
 8         result = prime * result + ((name == null) ? 0 : name.hashCode());
 9         return result;
10     }

对于超出int类型长度的long类型,只需要做一下位移操作,保证不超出int范围即可。

最后,奉上一段国外程序员给出的重写hashCode方法的原则:

1) Take a prime hash e.g. 5, 7, 17 or 31 (prime number as hash, results in distinct hashcode for distinct object)
2) Take another prime as multiplier different than hash is good.
3) Compute hashcode for each member and add them into final hash. Repeat this for all members which participated in equals.
4) Return hash

Read more: http://javarevisited.blogspot.com/2011/10/override-hashcode-in-java-example.html#ixzz3YHnchQTu

关于equals和hashcode方法

标签:

原文地址:http://www.cnblogs.com/mayongsheng/p/4455570.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!