2014-07-21 33 views
-1

我正在閱讀Effective Java Item 9,並決定自行運行示例代碼。但它的工作原理略有不同,具體取決於我如何插入一個新的對象,我不明白到底發生了什麼。該******中國一流的樣子:有效的Java項目9:重寫hashcode示例

public class PhoneNumber { 

private final short areaCode; 
private final short prefix; 
private final short lineNumber; 

public PhoneNumber(int areaCode, int prefix, int lineNumber) { 
    this.areaCode = (short)areaCode; 
    this.prefix = (short) prefix; 
    this.lineNumber = (short)lineNumber; 
} 

@Override public boolean equals(Object o) { 
    if(o == this) return true; 
    if(!(o instanceof PhoneNumber)) return false; 
    PhoneNumber pn = (PhoneNumber)o; 
    return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode; 
} 
} 

然後再根據這本書,爲的是當我試過了,

public static void main(String[] args) { 

     HashMap<PhoneNumber, String> phoneBook = new HashMap<PhoneNumber, String>(); 
     phoneBook.put(new PhoneNumber(707,867,5309), "Jenny"); 
     System.out.println(phoneBook.get(new PhoneNumber(707,867,5309))); 
    } 

這版畫「空」,並在書中解釋,因爲HashMap中有緩存的優化散列碼與每個條目相關聯,並且如果散列碼不匹配,則不檢查對象相等性。對於我,這說得通。但是,當我這樣做:

public static void main(String[] args) { 

     PhoneNumber p1 = new PhoneNumber(707,867,5309); 
     phoneBook.put(p1, "Jenny"); 
     System.out.println(phoneBook.get(new PhoneNumber(707,867,5309))); 
    } 

現在返回「珍妮」。你能解釋爲什麼它在第二種情況下沒有失敗嗎?

+0

必須以某種方式相同的實例,除非實際上重寫'int hashCode()'。 –

+0

不,我不是在任何地方重寫hashCode。這是這個例子的重點,如果你不重寫hashCode,它將無法正常工作。我知道它會返回「Jenny」,因爲這兩個實例是相同的實例,但想知道與第一個實例相比有什麼不同。 – pandagrammer

+4

這不可能是你正在運行的代碼 - 你在開始時在語句結尾處缺少分號。這讓我想知道你正在運行的實際代碼中* else *是不同的。我們無法幫助您理解我們無法看到的代碼。 (只是添加分號,我不能重現你的問題。) –

回答

1

經驗豐富的行爲可能取決於的Java版本,用於運行應用程序,因爲自從Object.hashcode()一般違約的情況,其結果是實現相關廠商

一個可能的解釋(以一個可能實現的HashMap):

HashMap類在其內部實現把對象(鍵)基於其哈希碼不同的桶。當查詢元素或者檢查映射中是否包含密鑰時,首先根據查詢密鑰的哈希碼查找合適的桶。桶內的對象以順序方式檢查,而在桶內僅使用equals()方法比較元素。

所以,如果你不覆蓋Object.hashcode()如果2個不同的對象產生默認的哈希碼可能會或不會確定相同的桶將是不確定的。如果有機會他們「指向」同一個桶,如果equals()方法表示它們相等,則仍然可以找到該密鑰。如果有機會他們「指向」兩個不同的桶,即使equals()方法說他們是平等的,你也不會找到該密鑰。

hashcode()必須被重寫以與重寫的equals()方法一致。只有在這種情況下,才能保證HashMap的正確,預期和一致的工作。

請閱讀javadoc的Object.hashcode()以瞭解您不能違反的合同。重點是,如果equals()爲另一個對象返回true,則hashcode()方法必須對這兩個對象返回相同的值。

+0

在我看到的實現中,它總是在調用equals之前檢查* exact *哈希碼匹配,而不僅僅是針對同一個桶。 –

+0

@JonSkeet根據供應商和版本的不同,實現可能會有所不同。 – icza

+0

同意,雖然我不認爲我見過一個實現,只是*桶。 (檢查哈希碼是否正確匹配非常便宜,並且可以輕鬆避免更多昂貴的「等號」調用。)您在考慮哪種實現? –

0

你能解釋爲什麼它在第二種情況下沒有失敗嗎?

簡而言之,它不保證失敗。第二個例子中的兩個對象最終可能具有相同的哈希代碼(純粹是巧合,或者更可能是由於編譯器優化,或者由於默認的JVM中的缺省工作原因)。這會導致你描述的行爲。

對於它的價值,我無法用我的編譯器/ JVM重現此行爲。

0

在你的情況下巧合JVM能夠找到兩個對象相同的hashCode。當我運行你的代碼時,在我的JVM中,這兩種情況都給出了空值。所以你的問題是因爲JVM而不是代碼。

每次重寫equils()方法時,最好重寫hashCode()。 我還沒有讀過Effective Java,我讀過Kathy Sierra的SCJP。所以如果你需要更多的細節,那麼你可以閱讀這本書。這真好。

+0

我想說這是因爲JVM。因爲在我的機器中它工作正常。 「你能解釋爲什麼它在第二種情況下沒有失敗嗎?」你對這個問題有什麼想法? – hcl

+0

我沒有親自採取。只是說。當我錯了時,隨時提供任何建議。 – hcl

+0

它很好有這樣的自發用戶在計算器上,歡迎登上:) – MarmiK

0

由於您尚未聲明phoneBook,所以上次剪下的代碼無法編譯。

兩種主要方法應該完全相同。由於新創建的HashMap的默認大小爲16,因此它將打印Jenny的概率爲1/16。詳細說明只有hashCode的低4位將被檢查。如果它們相等,則使用相等的方法。