在Java中hashCode的實現老是伴隨着equals,他們是緊密配合的,你要是本身設計了其中一個,就要設計另一個。固然在多數狀況下,這兩個方法是不用咱們考慮的,直接使用默認方法就能夠幫助咱們解決不少問題。可是在有些狀況,咱們必需要本身動手來實現它,才能確保程序更好的運做。html
粗略總結一下在JavaDoc中所規定hashcode方法的合約:算法
Objects that are equal must have the same hash code within a running process。數組
(在程序執行期間,若是兩個對象相等,那麼它們的哈希值必須相等)app
注意,下面兩條常見錯誤想法:eclipse
關於hashcode,你必須知道的三件事:ide
詳情見:The 3 things you should know about hashCode()性能
對於hashCode,咱們應該遵循以下規則:this
1. 在一個應用程序執行期間,若是一個對象的equals方法作比較所用到的信息沒有被修改的話,則對該對象調用hashCode方法屢次,它必須始終如一地返回同一個整數。spa
2. 若是兩個對象根據equals(Object o)方法是相等的,則調用這兩個對象中任一對象的hashCode方法必須產生相同的整數結果。.net
3. 若是兩個對象根據equals(Object o)方法是不相等的,則調用這兩個對象中任一個對象的hashCode方法,不要求產生不一樣的整數結果。但若是能不一樣,則可能提升散列表的性能。
對於equals,咱們必須遵循以下規則:
對稱性:若是x.equals(y)返回是「true」,那麼y.equals(x)也應該返回是「true」。
自反性:x.equals(x)必須返回是「true」。
傳遞性:若是x.equals(y)返回是「true」,並且y.equals(z)返回是「true」,那麼z.equals(x)也應該返回是「true」。
一致性:若是x.equals(y)返回是「true」,只要x和y內容一直不變,無論你重複x.equals(y)多少次,返回都是「true」。
任何狀況下,x.equals(null),永遠返回是「false」;x.equals(和x不一樣類型的對象)永遠返回是「false」。
hashcode:
常被系統用來快速檢索對象。
equals:
一、若是沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址;
二、String、Date等類對equals方法進行了重寫,它們比較的是所指向的對象的內容。
固然在重寫equals方法中,能夠指定比較對象的地址,若是這樣的話,就失去了重寫的意義,因此重寫,通常是比較對象的內容。
注意:equals方法不能做用於基本數據類型的變量。
至於hashcode與equals之間的關聯關係,咱們只須要記住以下便可:
所以,在重寫equals方法時,老是重寫hashCode方法。改寫後equals方法,使得兩個不一樣的實例在邏輯上是相等的;若是不重寫hashCode方法,則hashCode判斷兩個不一樣實例是不一樣的;致使違反「若是x.equals(y)返回「true」,那麼x和y的hashCode()必須相等。」
public class Person
{
private String name;
private int age;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
@Override
public boolean equals(Object other)
{
// 一、 自反性
if (other == this)
{
return true;
}
// 二、判斷空值
if (other == null)
{
return false;
}
// 三、對象所屬類的類型判斷
if (!getClass().equals(other.getClass()))
{
return false;
}
// 四、對象的類型轉換
Person person = (Person) other;
// 五、針對所需比較的域進行比較
if ((name.equals(person.name))&&(age==person.age))
{
return true;
}
return false;
}
}
如何選擇這兩種方式呢?
若是子類可以擁有本身的相等概念,則對稱性需求將強制採用getClass進行檢測。
若是由超類決定相等的概念,那麼就可使用instanceof進行檢測,這樣能夠在不一樣子類的對象之間進行相等的比較。
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沒有對null值判斷,但在其註釋有解釋:
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
一、把某個非零常數值(通常取素數),例如17,保存在int變量result中;二、對於對象中每個關鍵域f(指equals方法中考慮的每個域),計算int類型的哈希值c:
- boolean型,計算(f ? 0 : 1);
- byte,char,short型,計算(int)f;
- long型,計算(int) (f ^ (f>>>32));
- float型,計算Float.floatToIntBits(afloat);
- double型,計算Double.doubleToLongBits(adouble)獲得一個long,而後再執行long型的計算方式;
- 對象引用,遞歸調用它的hashCode方法;
- 數組域,對其中每一個元素調用它的hashCode方法。
三、步驟2中,計算獲得的散列碼保存到int類型的變量c中,而後再執行result=31*result+c;(其中31能夠自選,最好是素數)
四、最後返回result。
@Override
public int hashCode()
{
int result = 17;
result = 31 * result + age;
result = 31 * result + stringToHashCode(name);
return result;
}
private int stringToHashCode(String str)
{
int result = 17;
if (str.length()>0)
{
char[] value = str.toCharArray();
for (int i = 0; i < value.length; i++)
{
char c = value[i];
result = 31 * result + c;
}
}
return result;
}
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode()
{
int h = hash;
if (h == 0 && value.length > 0)
{
char val[] = value;
for (int i = 0; i < value.length; i++)
{
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
參考:
五、Java核心技術卷1