您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Java中如何使用HashCode,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
關于hashCode,維基百科中:
In the Java programming language, every class implicitly or explicitly provides a hashCode() method, which digests the data stored in an instance of the class into a single hash value (a 32-bit signed integer).
hashCode就是根據存儲在一個對象實例中的所有數據,提取出一個32位的整數,該整數的目的是用來標示該實例的唯一性。有點類似于MD5碼,每個文件都能通過MD5算法生成一個唯一的MD5碼。不過,Java中的hashCode并沒有真正的實現為每個對象生成一個唯一的hashCode,還是會有一定的重復幾率。
先來看看Object類,我們知道,Object類是java程序中所有類的直接或間接父類,處于類層次的最高點。在Object類里定義了很多我們常見的方法,包括我們要講的hashCode方法,如下
public final native Class<?> getClass(); public native int hashCode(); public boolean equals(Object obj) { return (this == obj); } public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
注意到hashCode方法前面有個native的修飾符,這表示hashCode方法是由非java語言實現的,具體的方法實現在外部,返回內存對象的地址。
在java的很多類中都會重寫equals和hashCode方法,這是為什么呢?最常見的String類,比如我定義兩個字符相同的字符串,那么對它們進行比較時,我想要的結果應該是相等的,如果你不重寫equals和hashCode方法,他們肯定是不會相等的,因為兩個對象的內存地址不一樣。
public int hashCode() { int h = hash; if (h == 0) { int off = offset; char val[] = value; int len = count; for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; } return h; }
其實這段代碼是這個數學表達式的實現
s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]
s[i]是string的第i個字符,n是String的長度。那為什么這里用31,而不是其它數呢?《Effective Java》是這樣說的:之所以選擇31,是因為它是個奇素數,如果乘數是偶數,并且乘法溢出的話,信息就會丟失,因為與2相乘等價于移位運算。使用素數的好處并不是很明顯,但是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,可以得到更好的性能:31*i==(i<<5)-i。現在的VM可以自動完成這種優化。
可以看到,String類是用它的value值作為參數來計算hashCode的,也就是說,相同的value就一定會有相同的hashCode值。這點也很容易理解,因為value值相同,那么用equals比較也是相等的,equals方法比較相等,則hashCode一定相等。反過來不一定成立。它不保證相同的hashCode一定有相同的對象。
一個好的hash函數應該是這樣的:為不相同的對象產生不相等的hashCode。
在理想情況下,hash函數應該把集合中不相等的實例均勻分布到所有可能的hashCode上,要想達到這種理想情形是非常困難的,至少java沒有達到。因為我們可以看到,hashCode是非隨機生成的,它有一定的規律,就是上面的數學等式,我們可以構造一些具有相同hashCode但value值不一樣的,比如說:Aa和BB的hashCode是一樣的。
如下代碼:
public class Main { public static void main(String[] args) { Main m = new Main(); System.out.println(m); System.out.println(Integer.toHexString(m.hashCode())); String a = "Aa"; String b = "BB"; System.out.println(a.hashCode()); System.out.println(b.hashCode()); } }
輸出結果:
Main@2a139a55 2a139a55 2112 2112
一般在重寫equal函數時,也要重寫hashCode函數,這是為什么呢?
來看看這個例子,讓我們創建一個簡單的類Employee
public class Employee { private Integer id; private String firstname; private String lastName; private String department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } }
上面的Employee類只是有一些非常基礎的屬性和getter、setter.現在來考慮一個你需要比較兩個employee的情形。
public class EqualsTest { public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints false in console System.out.println(e1.equals(e2)); } }
毫無疑問,上面的程序將輸出false,但是,事實上上面兩個對象代表的是通過一個employee。真正的商業邏輯希望我們返回true。
為了達到這個目的,我們需要重寫equals方法。
public boolean equals(Object o) { if(o == null) { return false; } if (o == this) { return true; } if (getClass() != o.getClass()) { return false; } Employee e = (Employee) o; return (this.getId() == e.getId()); }
在上面的類中添加這個方法,EauqlsTest將會輸出true。
So are we done?沒有,讓我們換一種測試方法來看看。
import java.util.HashSet; import java.util.Set; public class EqualsTest { public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints 'true' System.out.println(e1.equals(e2)); Set<Employee> employees = new HashSet<Employee>(); employees.add(e1); employees.add(e2); //Prints two objects System.out.println(employees); }
上面的程序輸出的結果是兩個。如果兩個employee對象equals返回true,Set中應該只存儲一個對象才對,問題在哪里呢?
我們忘掉了第二個重要的方法hashCode()。就像JDK的Javadoc中所說的一樣,如果重寫equals()方法必須要重寫hashCode()方法。我們加上下面這個方法,程序將執行正確。
@Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + getId(); return result; }
需要注意記住的事情
盡量保證使用對象的同一個屬性來生成hashCode()和equals()兩個方法。在我們的案例中,我們使用員工id。
eqauls方法必須保證一致(如果對象沒有被修改,equals應該返回相同的值)
任何時候只要a.equals(b),那么a.hashCode()必須和b.hashCode()相等。
兩者必須同時重寫。
關于“Java中如何使用HashCode”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。