在根類Object中,實現了equals()和hashCode()這兩個方法,默認:
equals()是對兩個對象的地址值進行的比較(即比較引用是否相同),用==實現。
hashCode():計算出對象實例的哈希碼。根類Object的hashCode()方法的計算依賴於對象實例的內存地址,即內存地址由哈希函數生成一個int值,故每一個Object對象的hashCode都是惟一的;固然,當對象所對應的類重寫了hashCode()方法時,結果就大相徑庭了。之因此有hashCode方法,是由於在批量的對象比較中,hashCode要比equals來得快,不少集合都用到了hashCode,好比Hashtable。html
在集合中,判斷兩個對象是否相等的規則是:
第一步,若是hashCode()相等,則查看第二步,不然不相等;
第二步,查看equals()是否相等,若是相等,則兩obj相等,不然仍是不相等。java
好比set集合存儲數據的時候是怎樣判斷存進的數據是否已經存在。使用equals()方法呢,仍是hashCode()方法。假如用equals(),那麼存儲一個元素就要跟已存在的全部元素比較一遍,好比已存入100個元素,那麼存101個元素的時候,就要調用equals方法100次。
但若是用hashcode()方法的話,每存一個數據就調用一次hashCode()方法,獲得一個hashCode值及存入的位置。若是該位置不存在數據那麼就直接存入,不然調用一次equals()方法,不相同則存,相同不存。這樣下來整個存儲下來不須要調用幾回equals方法,雖然多了一次hashCode方法,但相對於前面來說效率高了很多。程序員
由於Object的equals()方法默認是兩個對象的引用的比較,意思就是指向同一內存則相等,不然不相等;若是你如今須要利用對象裏面的值來判斷是否相等,則重載equals()方法。記住:String,Double、Integer、Math這些類已經重寫了equals()方法,比較的是對象的值。算法
若是不這樣作到話,就會違反Object.hashCode的通用約定:相等的對象必須具備相等的散列碼hashCode。根據一個類的equals方法,兩個大相徑庭的實例有可能在邏輯上是相等的,可是,根據Object類的hashCode方法,它們僅僅是兩個對象,對象hashCode方法返回兩個看起來是隨機的整數,而不是根據第二個約定要求的那樣,返回兩個相等的整數。從而致使該類沒法與全部基於散列值(hash)的集合類結合在一塊兒正常運做,這樣的集合類包括hashMap、HashSet和Hashtable。好比new一個對象,再new一個內容相等的對象,調用equals方法返回的true,但他們的hashCode值不一樣,將兩個對象存入HashSet中,hashCode值不一樣,均可以存進去,這樣set中包含兩個相等的對象。由於是先檢索hashCode值,相等的狀況下才會去比較equals方法。數組
Hash表數據結構常識:
1、哈希表基於數組。
2、缺點:基於數組的,數組建立後難以擴展。某些哈希表被基本填滿時,性能降低得很是嚴重。
3、沒有一種簡便的方法能夠以任何一種順序遍歷表中數據項。
4、若是不須要有序遍歷數據,而且能夠提早預測數據量的大小,那麼哈希表在速度和易用性方面是無與倫比的。緩存
HashMap和Hashtable,雖然它們有很大的區別,如繼承關係不一樣,對value的約束條件(是否容許null)不一樣,以及線程安全性等有着特定的區別,但從實現原理上來講,它們是一致的。因此,咱們只以Hashtable來講明:
在java中,存取數據的性能,通常來講固然是首推數組,可是在數據量稍大的容器選擇中,Hashtable將有比數組性能更高的查詢速度。具體緣由看下面的內容:
Hashtable在存儲數據時,通常先將該對象的HashCode和0x7FFFFFFF作與操做,由於一個對象的HashCode能夠爲負數,這樣操做後能夠保證它爲一個正整數。而後以Hashtable的長度取模,獲得該對象在Hashtable中的索引。安全
index = (o.hashCode() & 0x7FFFFFFF)%hs.length;
這個對象就會直接放在Hashtable的index位置,對於寫入,這和數組同樣,把一個對象放在其中的第index位置,但若是是查詢,通過一樣的算法,Hashtable能夠直接從第index取得這個對象,而數組卻要作循環比較。因此對於數據量稍大時,Hashtable的查詢比數組具備更高的性能。數據結構
事實上一個設計比較好的Hashtable,通常來講會比較平均地分佈每一個元素,由於Hashtable的長度老是比實際元素的個數按必定比例進行自增(負載因子通常爲0.75左右),這樣大多數的索引位置只有一個對象,而不多的位置會有幾個元素。可是,hash衝突很難徹底避免,能夠看hash。通常Hashtable中的每一個位置存放的是一個鏈表,對於只有一個對象的位置,鏈表只有一個首節點(Entry),Entry的next爲null,同時保存hashCode,key,value屬性,若是有相同索引的對象進來則會進入鏈表的下一個節點。若是同一個索引中有多個對象,根據HashCode和key能夠在該鏈表中找到一個和查詢的key相匹配的對象(equals方法)。
對於一個對象,若是具備不少屬性,把全部屬性都參與散列,顯然是一種笨拙的設計。由於對象的HashCode()方法被自動調用的不少,若是太多的對象參與了散列,那麼須要的時間將會增長不少。能夠挑選具備區分度的屬性計算hash值,或者設立緩存,只要當參與散列的對象改變時才從新計算,不然調用緩存的hashCode,這能夠從很大程度上提升性能。
默認的實現是將對象內存地址轉化爲整數做爲HashCode,這固然能保證每一個對象具備不一樣的HasCode,但java語言並不能讓程序員獲取對象內存地址。
請記住:若是你想有效的使用HashMap,你就必須重寫在其的hashCode()。函數
還有兩條重寫hashCode()的原則:性能