zoukankan      html  css  js  c++  java
  • 关于equals和hashcode方法

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

    所有引用类型都是继承自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的性能。

    那么如何重写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

  • 相关阅读:
    bzoj 3670: [Noi2014]动物园
    bzoj 2878: [Noi2012]迷失游乐园
    51nod 1348 乘积之和
    51nod 1514 美妙的序列
    AtCoder Grand Contest 002 D
    bzoj 3451 Normal
    LOJ #6119. 「2017 山东二轮集训 Day7」国王
    51nod 1752 哈希统计
    计蒜客 百度地图的实时路况
    Codeforces 549F Yura and Developers
  • 原文地址:https://www.cnblogs.com/mayongsheng/p/4455570.html
Copyright © 2011-2022 走看看