1. 程式人生 > >Java中的equals 和hashCode的理解

Java中的equals 和hashCode的理解

前幾天面試,被問了一個hashCode值相等,物件是否相等;物件相等,hashCode是否相等。

突然懵逼了,因為是面試,一下緊張,按照記憶中的說的,完全打錯,結果可想而知。 可見自己對這完全不理解,故重新認識一番。



Java對於eqauls方法和hashCode方法是這樣規定的: 

1、如果兩個物件相同,那麼它們的hashCode值一定要相同;
 
2、如果兩個物件的hashCode相同,它們並不一定相同(上面說的物件相同指的是用eqauls方法比較。)  

物件預設都extends Object ,先看Object對這兩個方法的定義:

public boolean equals(Object obj) {  
        return (this == obj);  
} 
是不是突然懵逼了,這明明比較物件的記憶體地址是否相等,也就是判斷是不是同一個物件。但是為什麼很多情況下都會用equals()去比較內容是否一致呢?
然後開啟String類的原始碼,會發現:

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
會發現,這種情況下,是String重寫了Object類的equals(),有了自己的實現方式,所以平常我們判斷字串內容是否相等用equals()是正確的。而且不止String類重寫了,Double、Float也都重寫了。  

至於hashCode()且看Object類中對它的定義:  

public native int hashCode();  
可以看到這是一個本地方法,返回的物件的地址值。

而再次拿出String類中的hashCode()方法:

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;
    }
最終也是返回一個計算出的hash值,用於判斷。 

Java中的集合(Collection)有兩類,一類是List,再有一類是Set。 前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。  

當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。 如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了, 就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。  

public class Test01 {

	public static void main(String[] args) {
		Name name1 = new Name("24");
		Name name2 = new Name("24");
		Set set = new HashSet();
		set.add(name1);
		System.out.println("********************111");
		set.add(name2);
		System.out.println("********************222");
		System.out.println(name1.equals(name2));
		System.out.println("********************333");
		System.out.println(name1.hashCode());
		System.out.println(name2.hashCode());
		System.out.println(set);
	}
	
}

class Name {
	private String id;
	public Name(String id) {
		this.id = id;
	}
	
	public String toString() {
		return this.id;
	}
	
	public boolean equals(Object obj) {
		if(obj instanceof Name) {
			Name name = (Name) obj;
			System.out.println("equals:"+name.id);
			return (id.equals(name.id));
		}
		return super.equals(obj);
	}
	
	public int hashCode() {
		Name name = (Name) this;
		System.out.println("Hash:"+name.id);
		return id.hashCode();
	}
	
}
執行結果如下:  

public class Test01 {

	public static void main(String[] args) {
		Name name1 = new Name("24");
		Name name2 = new Name("24");
		List list = new ArrayList();
		list.add(name1);
		System.out.println("********************111");
		list.add(name2);
		System.out.println("********************222");
		System.out.println(name1.equals(name2));
		System.out.println("********************333");
		System.out.println(name1.hashCode());
		System.out.println(name2.hashCode());
		System.out.println(list);
	}
	
}
執行結果如下:  

可以看出,set進行新增的時候,先去判斷hashCode,而list就不必判斷hashCode,它可以儲存重複資料,而set判斷後,如果有相同值,不進行儲存。  


總結,這些在學校都是最基礎的東西,可惜當初沒認真聽,後來也是覺得該怎麼用就怎麼用,也沒去思考一些最基本的東西,所以,還是要多理解。