轉自: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原理的精髓所在啊!