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呢,就是當你的物件作為索引的時候!