下面的程序包含了一個簡單的不可變類,它表示一個名字,其main方法將一個名字置于一個集合中,并檢查該集合是否確實包含了該名字。那么,這個程序到底會打印出什么呢?
import java.util.*;
public class Name {
private String first, last;
public Name(String first, String last) {
this.first = first;
this.last = last;
}
public boolean equals(Object o) {
if (!(o instanceof Name))
return false;
Name n = (Name)o;
return n.first.equals(first) && n.last.equals(last);
}
public static void main(String[] args) {
Set s = new HashSet();
s.add(new Name("Mickey", "Mouse"));
System.out.println(
s.contains(new Name("Mickey", "Mouse")));
}
}
一個Name實例由一個姓和一個名構(gòu)成。兩個Name實例在通過equals方法進行計算時,如果它們的姓相等且名也相等,則這兩個Name實例相等。姓和名是用在String中定義的equals方法來比較的,兩個字符串如果以相同的順序包含相同的若干個字符,那么它們就相等。因此,兩個Name實例如果表示相同的名字,那么它們就相等。例如,下面的方法調(diào)用將返回true:
new Name("Mickey", "Mouse").equals(new Name("Mickey", "Mouse"))
該程序的main方法創(chuàng)建了兩個Name實例,它們都表示Mickey Mouse。該程序?qū)⒌谝粋€實例放置到了一個散列集合中,然后檢查該集合是否包含第二個實例。這兩個Name實例是相等的,因此看起來該程序似乎應(yīng)該打印true。如果你運行它,幾乎可以肯定它將打印false。那么這個程序出了什么問題呢?
這里的bug在于Name違反了hashCode約定。這看起來有點奇怪,因為Name連hashCode都沒有,但是這確實是問題所在。Name類覆寫了equals方法,而hashCode約定要求相等的對象要具有相同的散列碼。為了遵守這項約定,無論何時,只要你覆寫了equals方法,你就必須同時覆寫hashCode方法[EJ Item 8]。
因為Name類沒有覆寫hashCode方法,所以它從Object那里繼承了其hashCode實現(xiàn)。這個實現(xiàn)返回的是基于標(biāo)識的散列碼。換句話說,不同的對象幾乎總是產(chǎn)生不相等的散列值,即使它們是相等的也是如此。所以說Name沒有遵守hashCode的約定,因此包含Name元素的散列集合的行為是不確定的。
當(dāng)程序?qū)⒌谝粋€Name實例放置到散列集合中時,該集合就會在某個散列位置上放置這個實例對應(yīng)的項。該集合是基于實例的散列值來選擇散列位置的,這個散列值是通過實例的hashCode方法計算出來的。
import java.util.*;
public class Name {
private String first, last;
public Name(String first, String last) {
this.first = first;
this.last = last;
}
public boolean equals(Object o) {
if (!(o instanceof Name))
return false;
Name n = (Name)o;
return n.first.equals(first) && n.last.equals(last);
}
public static void main(String[] args) {
Set s = new HashSet();
s.add(new Name("Mickey", "Mouse"));
System.out.println(
s.contains(new Name("Mickey", "Mouse")));
}
}
一個Name實例由一個姓和一個名構(gòu)成。兩個Name實例在通過equals方法進行計算時,如果它們的姓相等且名也相等,則這兩個Name實例相等。姓和名是用在String中定義的equals方法來比較的,兩個字符串如果以相同的順序包含相同的若干個字符,那么它們就相等。因此,兩個Name實例如果表示相同的名字,那么它們就相等。例如,下面的方法調(diào)用將返回true:
new Name("Mickey", "Mouse").equals(new Name("Mickey", "Mouse"))
該程序的main方法創(chuàng)建了兩個Name實例,它們都表示Mickey Mouse。該程序?qū)⒌谝粋€實例放置到了一個散列集合中,然后檢查該集合是否包含第二個實例。這兩個Name實例是相等的,因此看起來該程序似乎應(yīng)該打印true。如果你運行它,幾乎可以肯定它將打印false。那么這個程序出了什么問題呢?
這里的bug在于Name違反了hashCode約定。這看起來有點奇怪,因為Name連hashCode都沒有,但是這確實是問題所在。Name類覆寫了equals方法,而hashCode約定要求相等的對象要具有相同的散列碼。為了遵守這項約定,無論何時,只要你覆寫了equals方法,你就必須同時覆寫hashCode方法[EJ Item 8]。
因為Name類沒有覆寫hashCode方法,所以它從Object那里繼承了其hashCode實現(xiàn)。這個實現(xiàn)返回的是基于標(biāo)識的散列碼。換句話說,不同的對象幾乎總是產(chǎn)生不相等的散列值,即使它們是相等的也是如此。所以說Name沒有遵守hashCode的約定,因此包含Name元素的散列集合的行為是不確定的。
當(dāng)程序?qū)⒌谝粋€Name實例放置到散列集合中時,該集合就會在某個散列位置上放置這個實例對應(yīng)的項。該集合是基于實例的散列值來選擇散列位置的,這個散列值是通過實例的hashCode方法計算出來的。