2015-12-03 21 views
4

道歉的相當天真的問題,但我相信我自己的答案是天真的。我認爲密鑰(在HashTables中)是不可變的,因爲我們不想以某種方式意外地改變密鑰,因此混亂了HashTable的排序。這是一個正確的解釋嗎?如果是這樣,它怎麼會更正確?爲什麼鍵在Java中是不可變的?

回答

5

HashTable.put密鑰被散列,並將其一個其值被存儲在一個數段(其是關鍵值對列表)根據散列中的一個,例如是這樣的:

bucket[key.hashcode() % numberOfBuckets].add(key, value) 

如果鑰匙的插入後hashcode改變它才能再在錯誤的桶,你會再無法找到它,並在哈希表會錯誤地在任何get返回null該鍵。

旁白:瞭解一個哈希表的內部工作原理可以幫助你瞭解你的鑰匙優質hashcode功能的重要性。由於糟糕的哈希碼功能可能導致密鑰在桶中分佈不良。由於存儲桶只是列表,導致大量的線性搜索,大大降低了哈希表的有效性。例如這個可怕的hashcode函數將所有內容放在一個桶中,所以它實際上只是一個列表。

public int hashcode { return 42; /*terrible hashcode example, don't use!*/ } 

這也是原因之一質數出現在良好的哈希碼的功能,例如:

public int hashcode { 
    int hash = field1.hashcode(); 
    hash = hash*31 + field2.hashcode(); //note the prime 31 
    hash = hash*31 + field3.hashcode(); 
    return hash; 
} 
3

的總體思路是正確的,但不是它的細節。

鍵在哈希表中不一定是一成不變的,這是他們的hashCode()(和equals)方法調用,需要留不變的結果是一致的(用於哈希表的行爲不難看出,那是) 。

從一個高層次的點,這是因爲單向散列表工作:當插入(keyvalue)對,key的的hashCode用於內部找出一個‘桶’,其中價值將被放置。當被key檢索時,hashCode被再次計算,以找回存儲桶。

現在,如果在插入和retreival之間的任何時間點,稱hashCode變化的結果,在「查找鬥」會比「插入」鬥不同,事情不會表現預見的。

綜上所述,給予重點對象,看起來像這樣(兩個內部串組成的客體,但只有一個,partOfHashCode被考慮到的hashCode /等於):

public static class Key { 
    private String partOfHashCode; 
    private String notPartOfHashCode; 

    @Override 
    public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + ((partOfHashCode == null) ? 0 : partOfHashCode.hashCode()); 
    return result; 
    } 
    @Override 
    public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    Key other = (Key) obj; 
    if (partOfHashCode == null) { 
     if (other.partOfHashCode != null) 
     return false; 
    } else if (!partOfHashCode.equals(other.partOfHashCode)) 
     return false; 
    return true; 
    } 
} 

的很精緻以這種方式使用它:

public static void main(String[] args) { 

Map<Key, String> myMap = new HashMap<>(); 
Key key = new Key(); 
key.partOfHashCode = "myHash"; 

myMap.put(key, "value"); 

key.notPartOfHashCode = "mutation of the key, but not of its hash/equals definition"; 

System.out.println(myMap.get(key)); 
} 

(這會在控制檯中記錄「value」對象)。

但它不是蠻好用的這種方式

public static void main(String[] args) { 

    Map<Key, String> myMap = new HashMap<>(); 
    Key key = new Key(); 
    key.partOfHashCode = "myHash"; 

    myMap.put(key, "value"); 

    key.partOfHashCode = "mutation of the hashCode of the key"; 

    System.out.println(myMap.get(key)); 
} 

(最後這個例子可以登錄控制檯「空」)。

有關此主題的更多信息,還應閱讀hashCode/equals一致性。

0

在Java中沒有固有的保證,即鍵不可變。它甚至不保證它們的hashcode保持不變。但是如果你添加了一個可變的鑰匙,你就有麻煩了。假設您插入的密鑰的hashCode爲1.然後將其插入到對應於1的哈希桶中。然後更改該對象使其具有hashCode爲2並呼叫hashMap.get(key)。雖然對象仍在hashTable中,但系統將在對應於2的存儲區中查找,但不會在此處找到它。因爲它不會被發現,所以你甚至無法登錄remove

tl; dr爲使您的應用程序正常工作HashTable-鍵需要有固定的hashcode s,但您必須自己處理這一事實。

相關問題