hash code、equals和「==」三者的關係java
1) 對象相等則hashCode必定相等;
2) hashCode相等對象未必相等。c++
== 是比較地址是否相等,JAVA中聲明變量都是引用嘛,不一樣的引用,可能指向同一個地址。程序員
equals 是比較值是否相等。面試
1.若是是基本變量,沒有hashcode和equals方法,基本變量的比較方式就只有==,;
2.若是是變量,因爲在java中全部變量定義都是一個指向實際存儲的一個句柄(你能夠理解爲c++中的指針),在這裏==是比較句柄的地址(你能夠理解爲指針的存儲地址),而不是句柄指向的實際內存中的內容,若是要比較實際內存中的內容,那就要用equals方法,可是!!!
若是是你本身定義的一個類,比較自定義類用equals和==是同樣的,都是比較句柄地址,由於自定義的類是繼承於object,而object中的equals就是用==來實現的,你能夠看源碼。
那爲何咱們用的String等等類型equals是比較實際內容呢,是由於String等經常使用類已經重寫了object中的equals方法,讓equals來比較實際內容,你也能夠看源碼。
3. hashcode
在通常的應用中你不須要了解hashcode的用法,但當你用到hashmap,hashset等集合類時要注意下hashcode。
你想經過一個object的key來拿hashmap的value,hashmap的工做方法是,經過你傳入的object的hashcode在內存中找地址,當找到這個地址後再經過equals方法來比較這個地址中的內容是否和你原來放進去的同樣,同樣就取出value。
因此這裏要匹配2部分,hashcode和equals
但假如說你new一個object做爲key去拿value是永遠得不到結果的,由於每次new一個object,這個object的hashcode是永遠不一樣的,因此咱們要重寫hashcode,你能夠令你的hashcode是object中的一個恆量,這樣永遠能夠經過你的object的hashcode來找到key的地址,而後你要重寫你的equals方法,使內存中的內容也相等。。。算法
首先,從語法角度,也就是從強制性的角度來講,hashCode和equals是兩個獨立的,互不隸屬,互不依賴的方法,equals成立與hashCode相等這兩個命題之間,誰也不是誰的充分條件或者必要條件。
可是,從爲了讓咱們的程序正常運行的角度,咱們應當向Effective Java中所言
重載equals的時候,必定要(正確)重載hashCode
使得equals成立的時候,hashCode相等,也就是a.equals(b)->a.hashCode() == b.hashCode(),或者說此時,equals是hashCode相等的充分條件,hashCode相等是equals的必要條件(從數學課上咱們知道它的逆否命題:hashCode不相等也不會equals),可是它的逆命題,hashCode相等必定equals以及否命題不equals時hashCode不等都不成立。
因此,若是面試的時候,最好把hashCode與equals之間沒有強制關係,以及根據(沒有語法約束力的)規範的角度,應當作到...這兩層意思都說出來:P 編程
總結一下,equals()是對象相等性比較,hashCode()是計算對象的散列值,固然他們的依據是對象的屬性。數組
對於equals,通常咱們認爲兩個對象同類型而且全部屬性相等的時候纔是相等的,在類中必須改寫equals,由於Object類中的equals只是判斷兩個引用變量是否引用同一對象,若是不是引用同一對象,即便兩個對象的內容徹底相同,也會返回false。固然,在類中改寫這個equals時,你也能夠只對部分屬性進行比較,只要這些屬性相同就認爲對象是相等的。
對於hashCode,只要是用在和哈希運算有關的地方,前面不少兄弟都提到了,和equals同樣,在你的類中也應該改寫。固然若是兩個對象是徹底相同的,那麼他們的hashCode固然也是同樣的,可是象前面所述,規則能夠由你本身來定義,所以二者之間並無什麼必然的聯繫。
固然,大多數狀況下咱們仍是根據全部的屬性來計算hashCode和進行相等性比較。安全
HashMap實現了Map接口,該接口的做用主要是爲客戶提供三種方式的數據顯示:只查看keys列表;只查看values列表,或以key-value形式成對查看。Map接口並無定義數據要如何存儲,也沒有指定如何斷定key是同樣,所以並非全部的Map實現都會與hashCode方法扯上關係,如TreeMap即是要求對象實現Comparator接口,經過其compare方法來比對二者是否一致,而非hashCode及equals。同理,若是咱們本身實現Map接口,咱們也能夠直接使用數組進行數據存儲使用==斷定key值是否一致,依然能夠徹底知足Map接口的定義。數據結構
就是一個鍵值對應的集合,包含put,get,equals,hashCode
HashMap a = new HashMap();
a.put("name", "abcdef"); // key是name,value是字符串abcdef
HashMap是基於HashCode的
Hash容器的基本要求應該有以下幾點:多線程
知足Hash表的查找要求(廢話)
能支持從小數據量到大數據量的自動轉變(自動擴容)
使用掛鏈法解決衝突
在Hash表中,記錄在表中的位置和其關鍵字之間存在着一種肯定的關係。這樣 咱們就能預先知道所查關鍵字在表中的位置,從而直接經過下標找到記錄。
HashSet無序,不可重複,線程非同步。底層是哈希表結構
散列表(Hash table,也叫哈希表),是根據關鍵字(Key value)而直接進行訪問的數據結構。
也就是說,它經過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫作散列函數,存放記錄的數組叫作散列表。
HashMap底層就是散列表數據結構,即數組和鏈表的結合體,
底層是一個數組結構,數組中的每一項又是一個鏈表。這樣作有什麼好處呢?
數組可以提供對元素的快速訪問但不易於擴展(若是不知道元素腳標,還得進行遍歷查找),鏈表易於擴展但不能對其元素進行快速訪問。
怎樣作到一箭雙鵰,就是散列表數據結構
元素(key,value)在HashMap中被封裝進Entry數組。
put元素的時候,根據key的hash值定位元素在Entry數組中的索引,若是當前索引有元素,就經過equals方法進行比較,將元素存進當前Entry的鏈表中適當的位置。
get元素的時候,根據key的hash值定位元素在Entry數組中的索引,而後經過equals方法定位元素在鏈表中的位置,取出該元素。
性能分析:
由於元素的存取是經過hash算法進行的,因此速度都沒的說。
在查找操做中,惟一影響性能的是在鏈表中,但實際只要優化好了key對象hashCode跟equals方法,就會避免鏈表中的數據過多而致使查找性能變慢。
哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。若是詳細講解哈希算法,那須要更多的文章篇幅,我在這裏就不介紹了。
初學者能夠這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並非)。
這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。
若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了;若是這個位置上已經有元素了,
就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。
因此這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。
因此,Java對於eqauls方法和hashCode方法是這樣規定的:
一、若是兩個對象相同,那麼它們的hashCode值必定要相同;二、若是兩個對象的hashCode相同,它們並不必定相同 上面說的對象相同指的是用eqauls方法比較。
你固然能夠不按要求去作了,但你會發現,相同的對象能夠出如今Set集合中。同時,增長新元素的效率會大大降低。
Java中的集合(Collection)有兩類,一類是List,再有一類是Set。
你知道它們的區別嗎?前者集合內的元素是有序的,元素能夠重複;後者元素無序,但元素不可重複。
那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?
這就是Object.equals方法了。可是,若是每增長一個元素就檢查一次,那麼當元素不少時,後添加到集合中的元素比較的次數就很是多了。
hashcode這個方法是用來鑑定2個對象是否相等的。
那你會說,不是還有equals這個方法嗎?
不錯,這2個方法都是用來判斷2個對象是否相等的。可是他們是有區別的。
通常來說,equals這個方法是給用戶調用的,若是你想判斷2個對象是否相等,你能夠重寫equals方法,而後在代碼中調用,就能夠判斷他們是否相等了。簡單來說,equals方法主要是用來判斷從表面上看或者從內容上看,2個對象是否是相等。舉個例子,有個學生類,屬性只有姓名和性別,那麼咱們能夠認爲只要姓名和性別相等,那麼就說這2個對象是相等的。
hashcode方法通常用戶不會去調用,好比在hashmap中,因爲key是不能夠重複的,他在判斷key是否是重複的時候就判斷了hashcode這個方法,並且也用到了equals方法。這裏不能夠重複是說equals和hashcode只要有一個不等就能夠了!因此簡單來說,hashcode至關因而一個對象的編碼,就好像文件中的md5,他和equals不一樣就在於他返回的是int型的,比較起來不直觀。咱們通常在覆蓋equals的同時也要覆蓋hashcode,讓他們的邏輯一致。舉個例子,仍是剛剛的例子,若是姓名和性別相等就算2個對象相等的話,那麼hashcode的方法也要返回姓名的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。
要從物理上判斷2個對象是否相等,用==就能夠了。
1.hashMap去掉了HashTable 的contains方法,可是加上了containsValue()和containsKey()方法。
2.hashTable同步的,線程安全,而HashMap是非同步的,線程不安全,效率上逼hashTable要高。
3.hashMap容許空鍵值,而hashTable不容許。
4.HashTable中hash數組默認大小是11,增長的方式是 old*2+1。HashMap中hash數組的默認大小是16,並且必定是2的指數。
5.Hashtable是Dictionary的子類,HashMap是Map接口的一個實現類;
HashMap實現同步的方法:調用Collections的靜態方法Collections.synchronizedMap(Map map);,返回一個同步的Map,這個Map封裝了底層的HashMap的全部方法,使得底層的HashMap即便是在多線程的環境中也是安全的。
同時說一下,ArrayList和HashSet,他們也都不是同步的,都是線程不安全的,對其實現同步的方式和HashMap的方式相同,都容許使用null元素,ArrayList分配的初始空間爲10,HashSet分配的初始空間爲16