java怎么計算散列碼hashcode?


轉自:https://blog.csdn.net/qq_21430549/article/details/52225801

1.從HashMap說起

我們知道Map以鍵值對的形式來存儲數據。有一點值得說明的是,如果要使用我們自己的類作為鍵,我們必須同時重寫hashCode() 和 equals()兩個方法。HashMap使用equals方法來判斷當前的鍵是否與表中的鍵相同。equals()方法需要滿足以下5個條件

  • 自反性 x.equals(x) 一定返回true
  • 對稱性 x.equals(y)返回true,則y.equals(x) 也返回true
  • 傳遞性 x.equals(y)返回true,y.equals(z)返回true,則x.equals(y)返回true
  • 一致性 如果對象中的信息沒有改變,x.equals(y)要么一直返回true,要么一直返回false
  • 對任何不是null的x,想x.equals(null)一定返回false

2.散列

散列的價值在於速度。

假如鍵沒有按照一定的順序進行保存,那么查詢的時候就只能按照順序進行線性查詢,然而,線性查詢是最慢的查詢方式。所以,將鍵值按照一定的順序排序,並且使用二分查找能購有效的提升速度。散列在此之上,更近一步,他將鍵保存在數組中(數組的查詢速度最快),用數組來表示鍵的信息,但是由於Map的容量是可變的,而數組的容量是不變的。要解決這個問題,數組中存的並不是鍵本身,而是鍵對象生成的一個數字,將其作為數組的下標,這個數字就是散列碼。

而這種辦法所產生的問題就是下標重復。而我們的解決辦法就是配合equals來確定鍵值。

查詢的過程首先就是計算散列碼,然后用散列碼來查詢函數(下標),通常,我們的數組中保存的是值的list,因此,我們計算出散列碼之后,通過下表取到的對應部分的list,然后通過equals就可以快速找到鍵值。

3.HashCode

hashCode函數是用來生成散列碼的,我們看看Integer的計算方式(ps:我們自己的對象我們要選擇自己的方式)

    public static int hashCode(int value) { return value; }
  • 1
  • 2
  • 3

這里不在多說,我們自己的類有自己的散列碼實現就好。

4. 以HashMap的get方法來說明

    public V get(Object key) { if (key == null) { HashMapEntry<K, V> e = entryForNullKey; return e == null ? null : e.value; } int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { return e.value; } } return null; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • int hash = Collections.secondaryHash(key); 計算出散列碼
  • HashMapEntry
            K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { return e.value; }
  • 1
  • 2
  • 3
  • 4
  • 若地址相同,返回值,
  • 若hash值相等且equals返回true,返回值
  • 、、、、、、、、、、、、、、、、、、、
  • 二、理解hashCode()

    散列的價值在於速度:散列使得查詢得以快速執行。由於速度的瓶頸是對“鍵”進行查詢,而存儲一組元素最快的數據結構是數組,所以用它來代表鍵的信息,注意:數組並不保存“鍵”的本身。而通過“鍵”對象生成一個數字,將其作為數組的下標索引。這個數字就是散列碼,由定義在Object的hashCode()生成(或成為散列函數)。同時,為了解決數組容量被固定的問題,不同的“鍵”可以產生相同的下標。那對於數組來說?怎么在同一個下標索引保存多個值呢??原來數組並不直接保存“值”,而是保存“值”的 List。然后對 List中的“值”使用equals()方法進行線性的查詢。這部分的查詢自然會比較慢,但是如果有好的散列函數,每個下標索引只保存少量的值,只對很少的元素進行比較,就會快的多。

    不知道大家有沒有理解我上面在說什么。不過沒關系,下面會有一個例子幫助大家理解。不過我之前一直被一個問題糾結:為什么一個hashCode的下標存的會有多個值?因為hashMap里面只能有唯一的key啊,所以只能有唯一的value在那個下標才對啊。這里就要提出一個新的概念哈希沖突的問題,借用網上的一個例子:

    比如:數組的長度是5。這時有一個數據是6。那么如何把這個6存放到長度只有5的數組中呢。按照取模法,計算6%5,結果是1,那么就把6放到數組下標是1的位置。那么,7就應該放到2這個位置。到此位置,哈希沖突還沒有出現。這時,有個數據是11,按照取模法,11%5=1,也等於1。那么原來數組下標是1的地方已經有數了,是6。這時又計算出1這個位置,那么數組1這個位置,就必須儲存兩個數了。這時,就叫哈希沖突。沖突之后就要按照順序來存放了。所以這里Java中用的解決方法就是在這個hashCode上存一個List,當遇到相同的hashCode時,就往這個List里add元素就可以了。這才是hash原理的精髓所在啊!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM