Java中hashCode()的使用

關於hashCode()的一些知識,很早之前還是做了比較系統深入的研究,可惜時間長都忘了,現在再回顧一下吧,也記下來,以備以後的查閱。

爲什麼設計hashCode()

hashCode()返回的是一個值,我們稱之爲哈希值,記爲hashCode值。他的主要用途是在對對象進行散列的時候作爲key值輸入,所以,我們的每一個對象的hashCode值需要儘可能的不一樣。

JDK對基類Object的hashCode()方法,提供了一個默認的基礎對象引用地址的特定算法,來保證每一個對象的hashCode值不一樣。

hashCode值和HashSet、HashMap等的關係

這些集合類是通過元素的hashCode值和相應的Hash算法來散列對象的。

比如對HashSet而言,存入元素的流程爲:

獲取hashCode值,經過hash算法,找到對象應該存放的位置。如果位置爲空,將對象放入該位置;如果位置不爲空,使用equals()方法比較兩個對象,如果返回true,不再插入,如果返回false,根據hash衝突解決算法將對象存入其他位置。

測試代碼:

import java.util.HashSet;
import java.util.Set;

public class HashCodeTest {
	public static void main(String[] args) {
		Set<MyClass> set = new HashSet<MyClass>();
		MyClass class1 = new MyClass();
		MyClass class2 = new MyClass();
		
		System.out.println(class1.hashCode());
		System.out.println(class2.hashCode());
		
		set.add(class1);
		set.add(class2);
		
		System.out.println(set.size());
	}
}

class MyClass {
	@Override
	public int hashCode() {
		return 1;
	}
	@Override
	public boolean equals(Object obj) {
		return false;
	}
}

通過修改MyClass.equals()方法的返回結果,我們可以看到,在hashCode值一樣的情況下,set.size()不一樣。

爲什麼重寫了equals()方法就必須重寫hashCode()方法呢?

這個問題很重要!!!!!!

看過String源碼的人會發現,String重寫了Object的equals()和hashCode()兩個方法,當String的equals爲true的時候,hashCode值相等。

那爲什麼這麼做呢?

Object規範說了:

如果兩個對象調用equals()方法相等,那麼這兩個對象的hashCode()返回的結果也必須相等。

是不是對這些規範了什麼的不感冒呢,哈哈,還是從例子出發吧。

比如有很多個學生對象,規定學號相同的學生爲同一個學生,所以重寫了equals()方法;當把這些對象全部放入HashSet集合的時候,有兩個學生對象的學號相同,執行了add()方法之後,發現集合HashSet依然存在這兩個對象,這與我們的期望就不一致了。那怎麼辦呢?重寫hashCode()被,保證equals的兩個對象的hashCode值一樣就OK了。

怎麼重寫hashCode()方法呢?

首先要保證equals的對象hashCode值一樣;

其次,對不equals的對象要儘量保證hashCode值不同

下面介紹一種可參考的實現算法步驟:

1、定義一個返回值 int result=17(這個值一般取素數);

2、爲對象中每一個有意義的屬性值(參與equals()方法比較的域)算出一個散列碼;

3、合併

4、返回結果result即可。


關於每一個域的散列碼的獲取規律:

  • boolean型,計算(f ? 0 : 1);
  • byte,char,short型,計算(int)f;
  • long型,計算(int) (f ^ (f>>>32));
  • float型,計算Float.floatToIntBits(afloat);
  • double型,計算Double.doubleToLongBits(adouble)得到一個long,再執行[2.3];
  • 對象引用,遞歸調用它的hashCode方法;
  • 數組域,對其中每個元素調用它的hashCode方法。

代碼例子:

	@Override
	public int hashCode() {
		int result = 17;
		result = 31*result + no;
		result = 31*result + name.hashCode();
		result = 31*result + age;
		return result;
	}

如果hashCode計算開銷比較大,並且這個對象是不可變的,應該考慮把這個值緩存在對象內部,代碼如下:

	private volatile int hashCode;
	
	@Override
	public int hashCode() {
		int result = hashCode;
		if(result==0)  {
			result = 17;
			result = 31*result + no;
			result = 31*result + name.hashCode();
			result = 31*result + age;
		}
		return result;
	}


String類型的HashCode

String類型的HashCode比較特殊,是通過每一個字符轉化爲int類型計算出來的,所以,相同內容的String類型的HashCode相同,不管是new String("abc")方式還是"abc"方式。

其他類型的HashCode

其他類型的HashCode默認獲取的是native方法的HashCode值,不相同。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章