2015-06-20 36 views
0

我有,爲什麼我得到的輸出作爲第二線的原因掙扎:運行下面的代碼時,在HashMap的「2號線空」:爲什麼用HashMap中的鍵檢索這些值?

import java.util.*; 

class Dog { 
    public Dog(String n) {name = n;} 
    public String name; 
    public boolean equals(Object o) { 
    if((o instanceof Dog) && (((Dog)o).name == name)) { 
     return true; 
    } 
    else {return false;} 
    } 
    public int hashCode() {return name.length();} 
} 

public class HelloWorld{ 

public static void main(String []args){ 
    Map<Object, Object> m = new HashMap<Object, Object>(); 
    Dog d1 = new Dog("clover"); 
    m.put(d1, "Dog key"); 
    System.out.println("Line1: " + m.get(d1)); 
    d1.name = "magnolia"; 
    System.out.println("Line2: " + m.get(d1)); 
    d1.name = "clover"; 
    System.out.println("Line3: " + m.get(new Dog("clover"))); 
    d1.name = "arthur"; 
    System.out.println("Line4: " + m.get(new Dog("clover"))); 
} 
} 

顯示輸出爲:

一號線:狗鍵

線路2:空

3號線:狗鍵

線路4:空

是的,我知道修改實例變量名稱反過來會影響狗的實例的哈希碼,因爲我計算哈希碼的方式。但是,我正在使用與密鑰相同的實例!那麼,爲什麼get()方法不能找到相應的值呢?似乎一旦一對被推入HashMap中,密鑰將永久硬編碼!這是它應該如何工作,這意味着,一旦散列碼已經被確定爲一個值之前,將對放置在HashMap中,散列碼永遠不會再被修改?

+2

是的,當然。如果您嘗試使用修改的哈希碼獲取(),那麼HashMap應該如何知道匹配的內容? –

+0

您應該使用'equals'而不是'=='比較字符串(例如'Dog.name')。另外,你期望與你當前的'hashCode'實現有很多衝突。你可以使用'return name.hashCode()'來代替。 –

+0

Oliver是正確的。您重寫的'hashcode'方法會根據Dog的名稱字段返回值。由於'clover'返回6,'magnolia'返回8,所以當你調用get()時,由於前一個條目的哈希碼是6,與第二次(8)通過的哈希碼不同, ,它將無法找到相應的條目並返回null – Arkantos

回答

0

是的,我知道修改實例變量名反過來會影響Dog實例的哈希碼,因爲我計算哈希碼的方式。但是,我正在使用與密鑰相同的實例!那麼,爲什麼get()方法不能找到相應的值呢?

這個解釋會有些過於簡化,但它應該仍然說明這裏發生了什麼。將HashMap看作是一組鍵值對。 hashCode值用於決定在哪個索引處獲取/放置給定值。

例如,如果你的哈希碼返回7,那麼它將嘗試將值放在索引7的數組中。所以,假設您正在執行put操作,但索引7已滿。有幾種方法可以解決這個問題,但最簡單的方法是在每個數組索引處使用具有相同散列值的存儲桶(例如鏈接列表),然後將新值添加到存儲桶中。

現在讓我們假設您正在執行get操作。您檢查數組中與您的哈希值相對應的索引 - 但不能保證這是您正在查找的值(由於可能的哈希衝突)。您需要確保該位置的密鑰也是用於查找的密鑰的equal。如果密鑰不相等,那麼你一直在尋找(在桶中)。如果無處可查(即您搜索了整個存儲桶),則該值不在地圖中。

這就是代碼破壞的部分。您正在查看正確的存儲區(因爲您的密鑰的散列值與原始散列值相同),但當您執行「深層」比較以檢查您是否確實擁有正確的密鑰/值時,equals方法現在會返回false對。

這是它是如何工作的,這意味着,一旦哈希碼已被確定爲放置一對的HashMap之前的值,則哈希碼不能被再次修改?

當你明白你會如何使用數組(如上所述),它應該成爲明顯的是,其實這是預期的行爲,實現一個HashMap和變異的關鍵是非常糟糕的主意


一些旁註...

您應該使用equals的名字也比較:

((Dog)o).name.equals(name)) 

這裏是您目前有:

((Dog)o).name == name) 

那檢查是否name字符串是相同的實例而不是如果字符串具有相同的值。

您可以通過只返回你的邏輯運算的結果,簡化您的equals方法頗有幾分:

public boolean equals(Object o) { 
    return (o instanceof Dog) && (((Dog)o).name.equals(name)); 
} 
+1

是的,但是由於它們使用的是字符串,它與問題的行爲無關。 – Radiodef

+0

@Radiodef - 啊,好點。我剛剛看到用於比較字符串的'=='並跳到一個錯誤的結論。 – DaoWen

+0

謝謝。我將回顧之前讀過的水桶概念。而關於==,幸運的是這裏工作只是因爲我使用字符串文字。絕對肯定的,正確的方法是用equals()替換==。 – softwarelover