1. 程式人生 > >list\set等容器(集合)那裡重寫equals為什麼還要重寫hashCode方法

list\set等容器(集合)那裡重寫equals為什麼還要重寫hashCode方法

我們學些java j2se的時候為還說比較兩個引用是否值(內容)相等的時候不去重寫hashcode方法,只是重寫equals方法呢:

一下是單純重寫equals方法的例子:

/**
 * 測試重寫equals方法
 * @author Rick
 *
 */
public class EqualsUsage {
String name;

public EqualsUsage(String name) {
this.name = name;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}

public boolean equals(Object obj) {
String name = ((EqualsUsage)obj).getName();
return this.name == name;
}
}

以下是測試類:

/**
 * 驗證重寫equals的重寫方法
 * @author Administrator
 *
 */
public class Test {
public static void main(String[] args) {
EqualsUsage usage$1 = new EqualsUsage("Rick");
EqualsUsage usage$2 = new EqualsUsage("Rick");
EqualsUsage usage$3 = new EqualsUsage("Teddy");
System.out.println(usage$1.equals(usage$2));
System.out.println(usage$2.equals(usage$3));
}
}

輸出結果:

true
false

由上面例子來講,由於EqualsUsage的屬性都是Rick,因此有兩個引用的比較是true,因為我們重寫了Object的equals方法,他們比較的是引用的屬性值是否相等.

就好像兩個筐裝滿水果的整體當作是硬碟,兩個筐分別是不同的實體地址,第一個筐有蘋果和菠蘿,第二個筐有香蕉和菠蘿

當我們沒有重寫equals方法的時候,我們預設呼叫Object類的equals方法,如果這樣比較usage$1.equals(usage$2)    

Object的equals方法如下:

    /* @param   obj   the reference object with which to compare.
     * @return  {@code true} if this object is the same as the obj
     *          argument; {@code false} otherwise.
     * @see     #hashCode()
     * @see     java.util.HashMap
     */


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

就好像我們比較的是這兩個筐的菠蘿是不是在同一個筐(同一個實體地址)

如果現在我們的需求是,我們需要equals比較的是分別在這兩個筐的菠蘿到底是不是同一種水果(是不是同一物件),那我們就比較的就是它們的屬性啦,是否名字相同,是否顏色,外形特徵相同等等,重寫的equals方法就是這麼重寫的.

但是他們的引用(地址)還是不會變的.

而在list和set等學習中,為什麼不單隻重寫equals還要重寫hashCode呢,原因如下

比如,一個"扒"字,如果在字典查詢字典上對應的"扒",此時"pa"\"ba"這兩個發音既是"扒"的屬性,也是可以助你查詢到"扒"字的索引。因此,當我們程式接收到"扒"字的時候,我們重寫了equals獲取到他的屬性,知道操作的物件是什麼,重寫了hashcode,知道怎麼去找到這個"字"的位置。

如下例子:

package com.j2se.hashCodeProblem;


import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;


public class BasicContainer {
    public static void main(String[] args) {
   
        Collection c = new LinkedList();
        c.add("hello");
        c.add(new Name("f1","l1"));
        c.add(new Integer(100));
        c.add(new Name("f1","l1"));
        System.out.println(c);  //初始版本
        
        /*c.remove("hello"); //不論一下有沒有重寫equals方法都會被刪除,因為其本身已經重寫equals方法,下同!!
        c.remove(new Integer(100));
        
        System.out.println(c.remove(new Name("f1","l1")));//沒有重寫hashcode是不能刪除
        System.out.println(c);*/
        
        /*System.out.println(c);
        System.out.println(new Integer(100));
        System.out.println(new Integer(100) instanceof Integer);*/
        //System.out.println(100 instanceof Integer);  //編譯有誤
        //System.out.println(true instanceof Boolean); //編譯有誤
       /* System.out.println(new Boolean(true) instanceof Boolean);*/
        System.out.println(c);
        c.remove(new Name("f1","l1"));
        System.out.println(c);
    }


}


class Name implements Comparable<Name> {

    private String firstName;
    private String lastName;
    
    public Name(String firstName, String lastName) {
        this.firstName = firstName; 
        this.lastName = lastName;
    }
    
    public String getFirstName() {  
    return firstName;   
    }
    
    public String getLastName() { 
    return lastName;  
    }
    
    public String toString() {  
    return firstName + " " + lastName;
    }
    
    /**
     * 重寫equals方法
     */
    public boolean equals(Object obj) {
   if (obj instanceof Name) {
       Name name = (Name) obj;
       return (firstName.equals(name.firstName))
           && (lastName.equals(name.lastName));
   }
   return super.equals(obj);
    }
    
public int hashCode() {
   return firstName.hashCode();
}



public int compareTo(Name name) {
        Name n = (Name)o;
        int lastCmp = 
            lastName.compareTo(n.lastName);
        return 
             (lastCmp!=0 ? lastCmp :
              firstName.compareTo(n.firstName));
    }

}

可以再結合String原始碼對hashcode的具體實現進一步瞭解,

然而注意的是兩個物件的hashcode相同,equals比較也返回true,他們的實體地址仍不一定相同.

總結:什麼時候需要重寫hashcode呢,就是當你的物件作為索引的時候!