簡體   English   中英

當hashcode()實現返回常量值時,為什么哈希表會退化為鏈表?

[英]Why does a hashtable degenerate into a Linked List when a hashcode() implementation returns a constant value?

// The worst possible legal hash function - never use!
@Override public int hashCode() { return 42; }

這是合法的,因為它確保了相等的對象具有相同的哈希碼。 這很糟糕,因為它確保每個對象都具有相同的哈希碼。 因此,每個對象都會散列到同一個存儲桶,並且散列表會退化為鏈接列表。 應該以線性時間運行的程序改為以二次方運行。

我試圖弄清楚如何(引自第47頁,第9項,Joshua Bloch的Effective Java)。

我看到它的方式如下(考慮以下代碼):

Map<String, String> h = new HashMap<String,String>();
h.put("key1", "value1");
h.put("key1", "value2");

第二個h.put("key1",...)命令發生的情況如下:1。獲取key1的哈希碼2.獲取代表上述哈希碼的桶3.在該桶中,為每個對象調用equals方法,用於查找是否存在相同的對象。

這有點快,因為首先你找到對象的“組”(桶),然后找到實際的對象。

現在,當hashcode實現為ALL對象返回相同的整數(例如42以上)時,只有一個桶,並且需要在整個對象上逐個調用equals方法HashMap中/哈希表。 這與鏈表一樣糟糕,因為如果鏈表中的對象也是如此,則必須逐個比較(調用equals)每個對象。

有人說,這就是哈希表退化為鏈表的原因嗎?

(我為上述文本的冗長而道歉。我的概念中我不夠清楚地說明它更簡潔)

是的,你的理解似乎是准確的。 但是,它不像鏈接列表。 共享一個公共存儲桶的條目的實際內部實現一個普通的舊鏈表。 存儲桶將Map.Entry保存在列表的開頭,每個條目都有一個指向其存儲桶下一個占用者的前向指針。 (當然,為了實現內置於Java中的HashMap。)

HashTable是一個具有映射功能(hashCode)的數組。 插入數組時,您可以計算位置並在此處插入元素。

但是,hashCode不保證每個元素都有不同的位置,因此一些對象可能會發生碰撞(具有相同的地址),而hashTable必須解決它。 有兩種常見的方法,如何做到這一點。

單獨鏈接

在單獨的鏈接(在Java中使用)中,數組的每個索引都包含一個鏈表 - 因此每個存儲桶(位置)都具有無限容量。 因此,如果你的hashCode只返回一個值,你只使用一個like list => hashTable是一個鏈表。

線性探測

第二種方法是線性探測。 在線性探測中,內部數組實際上是正常的元素數組。 當您發現該位置已被占用時,您將迭代數組並將新元素放在第一個空位置。

所以我你的hashCode的impl為每個元素生成了一個含有的值,你只生成了colisions,因此你試圖將所有元素放在同一個索引上,因為它總是被占用,你迭代所有放置的元素並放置新元素在this structure的最后。 如果你再讀一遍,你在做什么,你必須看到,你只使用鏈表的另一個(你可以說是隱含的)實現。

為什么不這樣做

你真的不應該返回常量值,因為哈希表是為了提供O(1)預期的搜索和插入操作的復雜性而構建的(因為哈希函數為(幾乎)每個不同的對象返回一個不同的地址)。 如果只返回一個值,則對於兩個操作,實現都會降級為鏈接列表,其中包含O(n)

哈希表 - 如果使用正確 - 平均提供常量時間查找。 就時間復雜性而言,恆定時間和它一樣好。

鏈接列表提供線性時間查找。 線性時間(即依次查看每個元素)和它一樣糟糕。

當哈希表以Bloch描述的方式被濫用時,其查找行為退化為鏈表的行為,僅僅因為它實際上變成了鏈表。

關於其他操作可以說類似的事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM