HashMap 散列初体验
參考目錄:
1. HashMap 散列初體驗
2. 為什么HashMap 常用String 對象作key
3. HashMap 原理
4.自定義 hashCode()
5.HashMap 的性能因子
我們用Groundhog(土撥鼠)對象與Prediction(預報) 對象做了一個簡單的土撥鼠預測季節的例子。我們用Groundhog 對象作為map 的key,將該土撥鼠預測的季節(Prediction)信息作為map 的value。
import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import java.util.Random;public class SpringDetector {public static <T extends Groundhog> void detectSpring (Class<T> type) throws Exception{Constructor<T> constructor = type.getConstructor(int.class);Map<Groundhog,Prediction> map = new HashMap<>();for(int i = 0;i < 10;i++)map.put(constructor.newInstance(i),new Prediction());for (Groundhog key : map.keySet())System.out.println(key + " pridict: " + map.get(key));Groundhog Groundhog = constructor.newInstance(1);System.out.println("Looking up prediction for : " + Groundhog);if(map.containsKey(Groundhog))System.out.println(map.get(Groundhog));elseSystem.out.println("Key not found.");}public static void main(String[] args) throws Exception{detectSpring(Groundhog.class);} }class Groundhog{protected int number;public Groundhog(int number){this.number = number;}public String toString(){return "Groundhog #" + number;} }class Prediction{private static Random rand = new Random(40);private boolean shadow = rand.nextDouble() > 0.5;@Overridepublic String toString() {if(shadow)return "Winter will come!";elsereturn "Early Spring!";} }輸出
Groundhog #9 pridict: Winter will come!
Groundhog #0 pridict: Winter will come!
Groundhog #2 pridict: Early Spring!
Groundhog #5 pridict: Early Spring!
Groundhog #7 pridict: Early Spring!
Groundhog #4 pridict: Early Spring!
Groundhog #6 pridict: Winter will come!
Groundhog #1 pridict: Early Spring!
Groundhog #3 pridict: Winter will come!
Groundhog #8 pridict: Winter will come!
Looking up prediction for : Groundhog #1
Key not found.
上面是其中一次的輸出結果,在Groundhog 類中通過構造函數傳遞了一個標志數字,于是可以根據這個標志在HashMap 中查找Prediction :“我想要看 Groundhog #1 這只土撥鼠預測的季節情況。”在Prediction 類中提供了一個boolean 值 與一個toString 的方法。detectSpring () 方法使用反射機制來實例化從Groundhog 或從Groundhog 派生類出來的對象。
我們首先使用Groundhog 和 Prediction 類來填充map ,然后打印該map 中的數據。然后我們想要查看“ Groundhog #1”這只土撥鼠的季節預測情況。但是卻發現無法找到 “Groundhog #1” 這個鍵,從上面的輸出結果我們看出, “Groundhog #1” 是已經存在map 中了。為什么這只土撥鼠沒有工作呢? 問題在Groundhog 這個類自動繼承Object 類,所以在這里使用Object 中的hashCode() 方法生成散列碼,而它默認是根據對象的地址來計算散列碼。因此map 中的“ Groundhog #1”與要創建的查找“ Groundhog #1”兩個實例的散列碼是不同的,因此在map 中查找不到該土撥鼠預測季節的情況。
也許我們會認為我們只需要重寫Object 類中的hashCode() 方法即可。如果你這樣做,你會發現它仍然行不通,除非你同時覆蓋equals() 方法,因為它也是Object 類的一部分,HashMap 使用equals() 方法來判斷當前的鍵是否與表中的鍵相同。在這里我們簡單看一下Object 類中的equals() 方法與 hashCode() 方法:
equals
public boolean equals(Object obj)指示其他某個對象是否與此對象“相等”。
equals 方法在非空對象引用上實現相等關系:
????自反性:對于任何非空引用值 x,x.equals(x) 都應返回 true。
????對稱性:對于任何非空引用值 x 和 y,當且僅當 y.equals(x) 返回 true 時,x.equals(y) 才應返回 true。
????傳遞性:對于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 應返回 true。
????一致性:對于任何非空引用值 x 和 y,多次調用 x.equals(y) 始終返回 true 或始終返回 false,前提是對象上 equals 比較中所用的信息沒有被修改。
????對于任何非空引用值 x,x.equals(null) 都應返回 false。
Object 類的 equals 方法實現對象上差別可能性最大的相等關系;即,對于任何非空引用值 x 和 y,當且僅當 x 和 y 引用同一個對象時,此方法才返回 true(x == y 具有值 true)。
注意:當此方法被重寫時,通常有必要重寫 hashCode 方法,以維護 hashCode 方法的常規協定,該協定聲明相等對象必須具有相等的哈希碼。
hashCode
public int hashCode()返回該對象的哈希碼值。支持此方法是為了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
hashCode 的常規協定是:
在 Java 應用程序執行期間,在對同一對象多次調用 hashCode 方法時,必須一致地返回相同的整數,前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另一次執行,該整數無需保持一致。
如果根據 equals(Object) 方法,兩個對象是相等的,那么對這兩個對象中的每個對象調用 hashCode 方法都必須生成相同的整數結果。
如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那么對這兩個對象中的任一對象上調用 hashCode 方法不 要求一定生成不同的整數結果。但是,程序員應該意識到,為不相等的對象生成不同整數結果可以提高哈希表的性能。
實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數。(這一般是通過將該對象的內部地址轉換成一個整數來實現的,但是 JavaTM 編程語言不需要這種實現技巧。)
在這里特別強調如果你需要覆蓋Object 類中的equals() 方法,那也請你也覆蓋hashCode() 方法[我曾在阿里Java 開發手冊中也看到過這句話]。equals () 方法只是比較兩個對象的地址,因此如果我們要實現“ Groundhog #1” 查看預測季節情況,那么我們必須讓Groundhog 同時覆蓋Object 類中的equals() 方法與hashCode() 方法。
下面我們就來這樣做:重新定義一個Groundhog 2 繼承自Groundhog 類,然后覆蓋了equals() 方法與 hashCode() 方法,hashCode() 方法返回父類的number 作為散列碼而不是再根據對象的地址來計算,equals() 方法用來判斷對象是否相同。
public static void main(String[] args) throws Exception{detectSpring(Grounghog2.class);}class Groundhog 2 extends Groundhog{public Groundhog 2(int num){super(num);}@Overridepublic int hashCode() {return number;}@Overridepublic boolean equals(Object obj) {return obj instanceof Groundhog 2 && super.number ==((Groundhog 2) obj).number;} } //.......其余代碼省略輸出
Groundhog #0 pridict: Winter will come!
Groundhog #1 pridict: Early Spring!
Groundhog #2 pridict: Early Spring!
Groundhog #3 pridict: Winter will come!
Groundhog #4 pridict: Early Spring!
Groundhog #5 pridict: Early Spring!
Groundhog #6 pridict: Winter will come!
Groundhog #7 pridict: Early Spring!
Groundhog #8 pridict: Winter will come!
Groundhog #9 pridict: Winter will come!
Looking up prediction for : Groundhog #1
Early Spring!
根據上面的結果我們可以看出”Groundhog #”1 不再找不到了,我們找到了該只土撥鼠預測的季節情況。上面這個例子用到了反射與泛型,你們不覺得上面的代碼很優美嗎?具有很強的擴展性,O(∩_∩)O哈哈~。可見學會使用了泛型與反射是多么重要,少年多多努力吧!
參考書籍:
《Java 編程思想》Bruce Eckel 著 陳昊鵬 譯
總結
以上是生活随笔為你收集整理的HashMap 散列初体验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 介绍水果苹果的作文(介绍一种水果苹果作文
- 下一篇: 水果苹果介绍50字(水果苹果介绍)