[英]why this.hashCode() and super.hashCode() returns the same value in subclass?
[英]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.