java中equals方法和hashcode方法的區別和聯繫,以及爲何要重寫這兩個方法,不重寫會怎樣

1、在Object類中的定義爲:
public native int hashCode();
是一個本地方法,返回的對象的地址值。
可是,一樣的思路,在String等封裝類中對此方法進行了重寫。方法調用獲得一個計算公式獲得的 int值。

2、在重寫任何類得hashcode方法時必須遵循如下幾點:
一、在Java應用的同一次執行過程當中,同一對象被屢次調用,則他們的hashcode值必然相同。
而對於同一個應用的兩次不一樣的調用,它們的Hashcode值能夠相同,也有可能不一樣。

二、對於兩個對象來講,若是他們的equals方法比較返回true,那麼這兩個對象的hashcode必然相同。
這也解釋了爲何String類中,
若是兩個對象的equals方法相同,則他們的hashcode值必定相同。
三、對於兩個對象來講,若是使用equals方法返回爲false,則他們的hashcode的值有可能相等也可能不等,
(若是不一樣會提升性能,由於在集合中類判斷兩個對象是否相等,若是其hashcode不等就直接不用判斷equals方法了)

四、對於Object對象來講,不一樣的Object對象的hashcode是不一樣的,它們返回的是對象的地址,
equals返回的也是對象的地址。因此在本身定義的類中若是要添加到集合對象中,

最好是要重寫hashcode和equals方法,否則會自動繼承自Object類中的兩個方法根據對象地址來判斷。
在重寫本身定義的類時,一般是在類中的根據某個值如name.hashcode();來進行判斷。


3、以HashSet 爲例:
當咱們使用HashSet時,hashCode()方法就會被獲得調用,判斷已經存儲在集合中的對象的hashCode值是否與所增長。
對象的hashCode值一致,若是「不一致」則直接加進去(不用比較equals()提升效率),若是一致,則進行equals方法的比較,若是返回true,代表集合裏面已經有這個對象,不能添加進去了。若是是false表是集合裏面沒有這個對象,則能夠加進去。因此在重寫hashcode()或者equals() 方法的任何一個方法時,必須重寫另一個。
示例代碼:
/**
* People 手工重寫hashcode方法和equals方法 根據name來判斷 兩個對象是否相等。
*/
class People {
private String name;
public People(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
//若是是本身
if(this==obj){
return true ;
}
//若是是空
if(obj==null ){
return false;
}
//比較兩個People的名字是否相同
if(obj!=null && obj instanceof People){
if(((People)obj).name.equals(this.name))
return true ;
}
return false;
}
@Override
public int hashCode() {
// String的hashcode原本就是用來比較兩個字符是否相等
return name.hashCode();
}
}

從Object類的hashCode()和equals()方法講起:   

 

      最近看了Object類的源碼,對hashCode() 和equals()方法有了更深的認識。重寫equals()方法就必須重寫hashCode()方法的緣由,從源頭Object類講起就更好理解了。java

 

先來看Object關於hashCode()和equals()的源碼:程序員

 

 

 
[java]  view plain  copy
 
  1. public native int hashCode();  
 
[java]  view plain  copy
 
  1. public boolean equals(Object obj) {  
  2.     return (this == obj);  
  3. }  
     光從代碼中咱們能夠知道,hashCode()方法是一個本地native方法,返回的是對象引用中存儲的對象的內存地址,而equals方法是利用==來比較的也是對象的內存地址。從上邊咱們能夠看出,hashCode方法和equals方法是一致的。還有最關鍵的一點,咱們來看Object類中關於hashCode()方法的註釋:

 

 

 

 
[java]  view plain  copy
 
  1. /** 
  2.      * Returns a hash code value for the object. This method is 
  3.      * supported for the benefit of hash tables such as those provided by 
  4.      * {@link java.util.HashMap}. 
  5.      * <p> 
  6.      * The general contract of {@code hashCode} is: 
  7.      * <ul> 
  8.      * <li>Whenever it is invoked on the same object more than once during 
  9.      *     an execution of a Java application, the {@code hashCode} method 
  10.      *     must consistently return the same integer, provided no information 
  11.      *     used in {@code equals} comparisons on the object is modified. 
  12.      *     This integer need not remain consistent from one execution of an 
  13.      *     application to another execution of the same application. 
  14.      * <li>If two objects are equal according to the {@code equals(Object)} 
  15.      *     method, then calling the {@code hashCode} method on each of 
  16.      *     the two objects must produce the same integer result. 
  17.      * <li>It is <em>not</em> required that if two objects are unequal 
  18.      *     according to the {@link java.lang.Object#equals(java.lang.Object)} 
  19.      *     method, then calling the {@code hashCode} method on each of the 
  20.      *     two objects must produce distinct integer results.  However, the 
  21.      *     programmer should be aware that producing distinct integer results 
  22.      *     for unequal objects may improve the performance of hash tables. 
  23.      * </ul> 
  24.      * <p> 
  25.      * As much as is reasonably practical, the hashCode method defined by 
  26.      * class {@code Object} does return distinct integers for distinct 
  27.      * objects. (This is typically implemented by converting the internal 
  28.      * address of the object into an integer, but this implementation 
  29.      * technique is not required by the 
  30.      * Java™ programming language.) 
  31.      * 
  32.      * @return  a hash code value for this object. 
  33.      * @see     java.lang.Object#equals(java.lang.Object) 
  34.      * @see     java.lang.System#identityHashCode 
  35.      */  
  36.     public native int hashCode();  
    簡單的翻譯一下就是,hashCode方法通常的規定是:

 

 

 

 
[java]  view plain  copy
 
  1. 1.在 Java 應用程序執行期間,在對同一對象屢次調用 hashCode 方法時,必須一致地返回相同的整數,前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另外一次執行,該整數無需保持一致。      
  2. 2.若是根據 equals(Object) 方法,兩個對象是相等的,那麼對這兩個對象中的每一個對象調用 hashCode 方法都必須生成相同的整數結果。      
  3. 3.若是根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法不 要求必定生成不一樣的整數結果。可是,程序員應該意識到,爲不相等的對象生成不一樣整數結果能夠提升哈希表的性能。  
 

 

 

     再簡單的翻譯一下第二三點就是:hashCode()和equals()保持一致,若是equals方法返回true,那麼兩個對象的hasCode()返回值必須同樣。若是equals方法返回false,hashcode能夠不同,可是這樣不利於哈希表的性能,通常咱們也不要這樣作。重寫equals()方法就必須重寫hashCode()方法的緣由也就顯而易見了。數組

 

   假設兩個對象,重寫了其equals方法,其相等條件是屬性相等,就返回true。若是不重寫hashcode方法,其返回的依然是兩個對象的內存地址值,必然不相等。這就出現了equals方法相等,可是hashcode不相等的狀況。這不符合hashcode的規則。下邊,會介紹在集合框架中,這種狀況會致使的嚴重問題。app

 

    重寫的做用:

 
        若是重寫(用於需求,好比創建一個Person類,比較相等我只比較其屬性身份證相等就可無論其餘屬性,這時候重寫)equals,就得重寫hashCode,和其對象相等保持一致。若是不重寫,那麼調用的Object中的方法必定保持一致。
 
       1.  重寫equals()方法就必須重寫hashCode()方法主要是針對HashSet和Map集合類型。集合框架只能存入對象(對象的引用(基本類型數據:自動裝箱))。
 
        在向HashSet集合中存入一個元素時,HashSet會調用該對象(存入對象)的hashCode()方法來獲得該對象的hashCode()值,而後根據該hashCode值決定該對象在HashSet中存儲的位置。簡單的說:HashSet集合判斷兩個元素相等的標準是:兩個對象經過equals()方法比較相等,而且兩個對象的HashCode()方法返回值也相等。若是兩個元素經過equals()方法比較返回true,可是它們的hashCode()方法返回值不一樣,HashSet會把它們存儲在不一樣的位置,依然能夠添加成功。一樣:在Map集合中,例如其子類Hashtable(jdk1.0錯誤的命名規範),HashMap,存儲的數據是<key,value>對,key,value都是對象,被封裝在Map.Entry,即:每一個集合元素都是Map.Entry對象。在Map集合中,判斷key相等標準也是:兩個key經過equals()方法比較返回true,兩個key的hashCode的值也必須相等。判斷valude是否相等equal()相等便可。
 
      稍微提一句:(1)兩個對象,用==比較比較的是地址,需採用equals方法(可根據需求重寫)比較。
 
                              (2)重寫equals()方法就重寫hashCode()方法。
 
                               (3)通常相等的對象都規定有相同的hashCode。
 
                                hash:散列,Map關聯數組,字典
 
     2. 集合類都重寫了toString方法。String類重寫了equal和hashCode方法,比較的是值。
 

     用HashSet來驗證兩個需都重寫的必要性

 
      程序提供了三個類A,B,C,它們分別重寫了equals(),hashCode()兩個方法中的一個或所有。

class A {
String certificate;框架

public A(String certificate) {ide

this.certificate = certificate;
}性能

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
A other = (A) obj;
if (certificate == null) {
if (other.certificate != null)
return false;
} else if (!certificate.equals(other.certificate))
return false;
return true;
}ui

}this

class B {
String certificate;// 身份證號spa

public B(String certificate) {

this.certificate = certificate;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((certificate == null) ? 0 : certificate.hashCode());
return result;
}
}

class C {
String certificate;

public C(String certificate) {

this.certificate = certificate;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((certificate == null) ? 0 : certificate.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;
C other = (C) obj;
if (certificate == null) {
if (other.certificate != null)
return false;
} else if (!certificate.equals(other.certificate))
return false;
return true;
}

}

 

 
 
 

package day02;

import java.util.HashSet;

public class EqualsHashcodeDemo {
public static void main(String[] args) {

HashSet<A> hashSetA = new HashSet();
hashSetA.add(new A("123"));
hashSetA.add(new A("123"));
for (Object hs : hashSetA) { 
System.out.println(hs); 



HashSet<B> hashSetB = new HashSet<B>();
hashSetB.add(new B("345"));
hashSetB.add(new B("345"));
for (Object hs : hashSetB) { 
System.out.println(hs); 
}
HashSet<C> hashSetC = new HashSet<C>();
hashSetC.add(new C("456"));
hashSetC.add(new C("456"));
for (Object hs : hashSetC) { 
System.out.println(hs); 
}
/**
* public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char
* val[] = value;

* for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; }
* return h; }
*/

}
}

 

其結果爲:
 
     
 
     
 
     
[java]  view plain  copy
 

cn.edu.uestc.collection.B@1  

day02.A@15db9742

day02.A@6d06d69c  //第一個set(hashSetA)中居然把同一個元素放入了set中,兩次,且算出的hashcode值也不一致,這就說明將同一元素放到了同一個set的不一樣位置,裏面有兩個,一個set中的不一樣位置放了兩個相同的A;

day02.B@c613

day02.B@c613    //第二個set(hashSetB)也把同一個元素放入了set中,兩次,算出的hashcode值一致,這就說明將同一元素放到了同一個set的相同位置,放了兩次而已, 同一個set的相同位置放了兩個相同的B;

day02.C@c9f4  //第三個set(hashSetC)也把同一個元素放入了set中,兩次,可是set中只保存了一個C,說明只有重寫兩個方法,才能達到結果;

    從上邊的程序結果能夠看到,必需要同時重寫這兩個方法,要否則Set的特性就被破壞了。

 

 
 
 

     重寫hashCode()的原則

 
    (1)同一個對象屢次調用hashCode()方法應該返回相同的值;
 
    (2)當兩個對象經過equals()方法比較返回true時,這兩個對象的hashCode()應該返回相等的(int)值;
 
    (3)對象中用做equals()方法比較標準的Filed(成員變量(類屬性)),都應該用來計算hashCode值。
 
      計算hashCode值的方法:      
 
[java]  view plain  copy
 
  1. //f是Filed屬性  
  2. boolean    hashCode=(f?0:1)  
  3. (byte,short,char,int)      hashCode=(int)f  
  4. long       hashCode=(int)(f^(f>>>32))  
  5. float       hashCode=Float.floatToIntBits(f)  
  6. double   hashCode=(int)(1^(1>>>32))  
  7. 普通引用類型    hashCode=f.hashCode()  
       將計算出的每一個Filed的hashCode值相加返回,爲了不直接相加產生的偶然相等(單個不相等,加起來就相等了),爲每一個Filed乘以一個質數後再相加,例若有:
 
[java]  view plain  copy
 
  1. return  f1.hashCode()*17+(int)f2.13  
     
 
    查看String源碼,看hashCode()d的實現方法:
 
 
 
 
 
[java]  view plain  copy
 
  1. /** 
  2.    * Returns a hash code for this string. The hash code for a 
  3.    * <code>String</code> object is computed as 
  4.    * <blockquote><pre> 
  5.    * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 
  6.    * </pre></blockquote> 
  7.    * using <code>int</code> arithmetic, where <code>s[i]</code> is the 
  8.    * <i>i</i>th character of the string, <code>n</code> is the length of 
  9.    * the string, and <code>^</code> indicates exponentiation. 
  10.    * (The hash value of the empty string is zero.) 
  11.    * 
  12.    * @return  a hash code value for this object. 
  13.    */  
  14.   public int hashCode() {  
  15.       int h = hash;  
  16.       if (h == 0 && value.length > 0) {  
  17.           char val[] = value;  
  18.   
  19.           for (int i = 0; i < value.length; i++) {  
  20.               h = 31 * h + val[i];  
  21.           }  
  22.           hash = h;  
  23.       }  
  24.       return h;  
  25.   }  
 
[java]  view plain  copy
 
  1. public class A {  
  2.     @Override  
  3.     public boolean equals(Object obj) {  
  4.         return true;  
  5.     }  
  6. }  

[java]  view plain  copy
 
  1. public class B {  
  2.     @Override  
  3.     public int hashCode() {  
  4.         return 1;  
  5.     }  
  6. }  

[java]  view plain  copy
 
  1. public class C {  
  2.     @Override  
  3.     public int hashCode() {  
  4.         return 2;  
  5.     }  
  6.   
  7.     @Override  
  8.     public boolean equals(Object obj) {  
  9.         return true;  
  10.     }  
  11. }  
 
    
[java]  view plain  copy
  1. public class HashSetTest {  
  2.     public static void main(String[] args) {  
  3.         HashSet hashSet = new HashSet();  
  4.         hashSet.add(new A());  
  5.         hashSet.add(new A());  
  6.         hashSet.add(new B());  
  7.         hashSet.add(new B());  
  8.         hashSet.add(new C());  
  9.         hashSet.add(new C());  
  10.         for (Object hs : hashSet) {  
  11.             System.out.println(hs);  
  12.         }  
  13.         //HashSet重寫了toString()方法  
  14. //        System.out.println(hashSet);  
  15.     }  
  16. }  
     其結果爲:
 
 
 
[java]  view plain  copy
 
  1. cn.edu.uestc.collection.B@1  
  2. cn.edu.uestc.collection.B@1  
  3. cn.edu.uestc.collection.C@2  
  4. cn.edu.uestc.collection.A@3f84246a  
  5. cn.edu.uestc.collection.A@18a9fa9c  
  6. Process finished with exit code 0  
    從上邊的程序結果能夠看到,必需要同時重寫這兩個方法,要否則Set的特性就被破壞了。
相關文章
相關標籤/搜索