hashcode與equals

1.什麼是hashcode

hashCode是jdk根據對象的地址或者字符串或者數字算出來的int類型的數值,也就是哈希碼,哈希碼並非徹底惟一的,它是一種算法,讓同一個類的對象按照本身不一樣的特徵儘可能的有不一樣的哈希碼,但不表示不一樣的對象哈希碼徹底不一樣。
在Java中,哈希碼錶明對象的特徵。
例如:
String str1 = 「aa」, str1.hashCode= 3104 String str2 = 「bb」, str2.hashCode= 3106 String str3 = 「aa」, str3.hashCode= 3104 根據HashCode由此可得出 str1!=str2, str1==str3

 

不一樣的對象有不一樣的哈希碼算法例如: 
一、Object類的hashCode返回對象的內存地址通過處理後的結構,因爲每一個對象的內存地址都不同,因此哈希碼也不同。 
二、String類的hashCode根據String類包含的字符串的內容,根據一種特殊算法返回哈希碼,只要字符串所在的堆空間相同,返回的哈希碼也相同。 
三、Integer類,返回的哈希碼就是Integer對象裏所包含的那個整數的數值,例如: 
Integer i1=new Integer(100); 
i1.hashCode的值就是100 。因而可知,2個同樣大小的Integer對象,返回的哈希碼也同樣。 
由此能夠看出Java中的hashCode方法就是根據必定的規則將與對象相關的信息(好比對象的存儲地址,對象的 字段等)映射成一個數值,這個數值稱做爲散列值。javascript

2.hashcode在Java中的相關引用

在Java中object類的hashCode方法定義以下:
public native int hashCode();

有此能夠看出object類並未對其具體實現,該方法的返回值是int,是本地方法。 
對於哈希碼自己來講就是一種爲了提升查找效率的一種算法,所以hashcode適合用於絕大多數的容器,例如:HashSet、HashMap以及HashTable。 
舉個例子: 
當咱們向集合中插入一個對象(不容許重複),若是該對象已經存在的狀況下不容許插入,在數據量大的狀況下調用equals()方法就形成 了效率緩慢問題。使用hashcode(),會先算出新增對象的哈希值若是沒有相等的哈希值在該集合中那麼就新增,若是存在相應的哈希值,則調用equals()方法判斷是否相等。 
下面是hashMap的源碼:java

public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }

從put方法的具體實現可知,會先調用hashCode方法獲得該元素的hashCode 值,而後查看table中是否存在該hashCode值,若是存在則調用equals方法從新肯定是否存在該元素,若是存在,則更新value值,不然將 新的元素添加到HashMap中。從這裏能夠看出,hashCode方法的存在是爲了減小equals方法的調用次數,從而提升程序效率。面試

3.equals方法

object類中定義以下:算法

public boolean equals(Object obj) { return (this == obj); }

在Object這個類裏面提供的Equals()方法默認的實現是比較當前對象的引用和你要比較的那個引用它們指向的是不是同一個對象,例如比較流行的面試問題equals和==的區別,在object中二者是相等的都是對一個對象的指向進行比較。可是在String中對其進行了重寫:ui

public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }

這樣在String中就變成了對值的比較,比較二者的value是否相等。 
通常咱們在設計一個類時,須要重寫父類的equals方法,在重寫這個方法時,須要按照如下幾個規則設計: 
一、自反性:對任意引用值X,x.equals(x)的返回值必定爲true. 
二、對稱性:對於任何引用值x,y,當且僅當y.equals(x)返回值爲true時,x.equals(y)的返回值必定爲true; 
三、傳遞性:若是x.equals(y)=true, y.equals(z)=true,則x.equals(z)=true 
四、一致性:若是參與比較的對象沒任何改變,則對象比較的結果也不該該有任何改變 
五、非空性:任何非空的引用值X,x.equals(null)的返回值必定爲falsethis

4.二者類似與比較

1)對於equals()與hashcode(),比較通用的規則: 
①兩個obj,若是equals()相等,hashCode()必定相等 
②兩個obj,若是hashCode()相等,equals()不必定相等spa

2)在設計一個類的時候每每須要重寫equals方法,好比String類,在重寫equals方法的同時,必須重寫hashCode方法。 
若是咱們對一個對象重寫了euqals,意思是隻要對象的成員變量值都相等那麼euqals就等於true,但不重寫hashcode,那麼咱們再new一個新的對象,當原對象.equals(新對象)等於true時,二者的hashcode倒是不同的,由此將產生了理解的不一致,如在存儲散列集合時(如Set類),將會存儲了兩個值同樣的對象,致使混淆,所以,就也須要重寫hashcode()。 
通俗一點就是在例如hashMap集合中put一個對象的時候其實就是將一個對象的值(屬性、哈希值)放入,若是在根據這個對象get取出來時也會考慮它的哈希值,可是get時若是沒有重寫hashcode()方法,你的新對象就是一個新的哈希值與原對象不想等,應該get時確定爲空。 
下面是hashMap中的get方法:設計

public V get(Object key) { if (key == null) return getForNullKey(); Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); } final Entry<K,V> getEntry(Object key) { int hash = (key == null) ? 0 : hash(key); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }

 

 

能夠看出get傳入對象時會去掉用getEntry方法,getEntry時先算出hash值,若是在容器中不存在該哈希值值直接返回空。code

相關文章
相關標籤/搜索