咱們時常會判斷一個元素是否相等重複,能夠用equals方法。html
每增長一個元素,咱們就能夠經過equals方法判斷集合中的每個元素是否重複,可是若是集合中有10000個元素了,咱們每添加一個元素的時候,就須要進行10000此的equals方法的調用,顯示效率很是的低下了。java
因而基於這種問題,java集合的設計者採用了哈希表來實現。算法
哈希表也稱爲散列算法,是依據數據特定算法產生的結果直接指定到一塊地址上,這個結果由hashCode方法產生。數據庫
這樣一來,當集合每添加一個新的元素的時候,就能夠經過hashCode方法直接定位到該存放的物理位置上,而不須要大量的equals板的比較。性能
上面說到了hashCode方法,它是Object類中的一個被native修飾的方法,優化
那麼也就是說,咱們每一個對象都會繼承了這個方法,咱們也就能夠重寫它了spa
Object類的hashCode方法代碼:設計
public native int hashCode();
hashCode的比較方式code
好比下方是在用HashSet存值htm
下面以簡單的圖來表示
這裏有A B C D四個對象,分別經過hashCode方法產生了3個值
注意A和B對象調用hashCode產生的值是相同的,即 A.hashCode = B.hashCode()= 0x001
發生了哈希衝突,這時候因爲最早插入了A,在插入B的時候,咱們發現B要插入A的位置,而A已經插入,也就是這個位置已經有對象了。
這個時候就經過調用equals方法判斷A和B是否相同,若是相同就不插入B,若是不一樣則將B插入到A後面的位置。
因此對於equals方法和hashCode方法有以下的要求:
所以獲得如下結論
- 兩個對象相等,其hashCode必定相同
- 兩個對象不相等,其hashCode可能相等
- hashCode相等的兩個對象,不必定相同
- hashCode不相等的兩個對象,必定不一樣
可能會有人疑問,對於不能重複的集合,爲何不直接經過 hashCode 對於每一個元素都產生惟一的值,若是重複就是相同的值,這樣不就不須要調用 equals 方法來判斷是否相同了嗎?
實際上對於元素不是不少的狀況下,直接經過 hashCode 產生惟一的索引值,經過這個索引值能直接找到元素,並且還能判斷是否相同。好比數據庫存儲的數據,ID 是有序排列的,咱們能經過 ID 直接找到某個元素,若是新插入的元素 ID 已經有了,那就表示是重複數據,這是很完美的辦法。但現實是存儲的元素很難有這樣的 ID 關鍵字,也就很難這種實現 hashCode 的惟一算法,再者就算能實現,可是產生的 hashCode 碼是很是大的,這會大的超過 Java 所能表示的範圍(由於返回值是int類型,大小隻能是232),很佔內存空間,因此也是不予考慮的。
咱們應該注意:
下面是String的hashCode實現
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
這裏有個數字 31 ,爲何選擇31做爲乘積因子,並且沒有用一個常量來聲明?主要緣由有兩個:
①、31是一個不大不小的質數,是做爲 hashCode 乘子的優選質數之一。
②、31能夠被 JVM 優化,31 * i = (i << 5) - i。由於移位運算比乘法運行更快更省性能。
具體解釋能夠參考這篇文章。
ps:
對於Map集合,咱們能夠選擇Java中的基本類型,還有引用類型String做爲key,由於它們都按照規範重寫了equals方法和hashCode方法。
可是若是咱們自定義對象做爲key,那麼必定要覆蓋equals方法和hahshCode方法,要否則會有未知的suprise等着你。
原文出處:https://www.cnblogs.com/arebirth/p/schashcodemethod.html