關於Hashcode的做用
總的來講,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素能夠重複;後者元素無序,但元素不可重複。
要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?這就是Object.equals方法了。可是,若是每增長一個元素就檢查一 次,那麼當元素不少時,後添加到集合中的元素比較的次數就很是多了。也就是說,若是集合中如今已經有1000個元素,那麼第1001個元素加入集合時,它 就要調用1000次equals方法。這顯然會大大下降效率。
因而,Java採用了哈希表的原理。哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。若是這個位置上沒有元素,它就能夠 直接存儲在這個位置上,不用再進行任何比較了;若是這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了;不相同,也就是發生了Hash key相同致使衝突的狀況,那麼就在這個Hash key的地方產生一個鏈表,將全部產生相同hashcode的對象放到這個單鏈表上去,串在一塊兒。因此這裏存在一個衝突解決的問題(不多出現)。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。
因此,Java對於eqauls方法和hashCode方法是這樣規定的:
一、若是兩個對象相等,那麼它們的hashCode值必定要相等;
二、若是兩個對象的hashCode相等,它們並不必定相等。
上面說的對象相等指的是用eqauls方法比較。
============================================================
如何理解hashCode的做用:
============================================================
以java.lang.Object來理解,JVM每new一個Object,它都會將這個Object丟到一個Hash哈希表中去,這樣的話,下次作Object的比較或者取這個對象的時候,它會根據對象的hashcode再從Hash表中取這個對象。這樣作的目的是提升取對象的效率。具體過程是這樣:
1.new Object(),JVM根據這個對象的Hashcode值,放入到對應的Hash表對應的Key上,若是不一樣的對象確產生了相同的hash值,也就是發生了Hash key相同致使衝突的狀況,那麼就在這個Hash key的地方產生一個鏈表,將全部產生相同hashcode的對象放到這個單鏈表上去,串在一塊兒。
2.比較兩個對象的時候,首先根據他們的hashcode去hash表中找他的對象,當兩個對象的hashcode相同,那麼就是說他們這兩個對象放在Hash表中的同一個key上,那麼他們必定在這個key上的鏈表上。那麼此時就只能根據Object的equal方法來比較這個對象是否equal。當兩個對象的hashcode不一樣的話,確定他們不能equal.
============================================================
改寫equals時老是要改寫hashCode
============================================================
java.lang.Object中對hashCode的約定:
1. 在一個應用程序執行期間,若是一個對象的equals方法作比較所用到的信息沒有被修改的話,則對該對象調用hashCode方法屢次,它必須始終如一地返回同一個整數。
2. 若是兩個對象根據equals(Object o)方法是相等的,則調用這兩個對象中任一對象的hashCode方法必須產生相同的整數結果。
3. 若是兩個對象根據equals(Object o)方法是不相等的,則調用這兩個對象中任一個對象的hashCode方法,不要求產生不一樣的整數結果。但若是能不一樣,則可能提升散列表的性能。 java
(如下的測試代碼,我稍微修改過) 算法
import java.util.*; public class AboutHashCode { public static void main(String[] args) { HashSet stringSet = new HashSet<V>(); for(int i=0; i<10; i++) stringSet.add(new String("test"));//默認重寫equals().hashcode() //產生相同的hashcode,並且equals返回true因此不加進去; System.out.println(stringSet.size()); HashSet vSet = new HashSet<V>(); for (int i = 0; i < 10; i++) { vSet.add(new V(i/2)); } System.out.println(vSet.size()); } public static class V { int i; public V(int i) { this.i = i; } public int getI() { return this.i; } public boolean equals(Object o) { V v = (V) o; System.out.print("hashcode相同的,而後才執行的equals()方法的!"); System.out.println(v.getI() == this.i); return v.getI() == this.i; } //若是不重寫,將會產生不一樣的hashcode,因此能夠加進set裏面 public int hashCode() { System.out.println("先執行hashCode()方法的!"); return i; } } }