起因:故嘗試調試下HashMap實現原理,打印出transient Entry<K,V>[] table 變量的變化情況
一,在hashmap中加入打印調試信息
hashmap的實現就是用一個Entry的對象數組Entry中存next形成鏈,鏈用於儲存key有相同hashcode但key的equas不同的entry,這個網上有很多相關分析;
那么我現在嘗試在hashmap這個類中加入監控信息用來展示它的實現原理
打開JDK的源碼(基於1.6),並在HashMap類中加入新的方法用於打印調試信息,即HashMap中table對象數組變量的變化
1 public String getTableChageInfor(String messge){ 2 StringBuffer tableInfor=new StringBuffer("========"); 3 tableInfor.append(messge); 4 tableInfor.append("===start==============="); 5 tableInfor.append("\n"); 6 tableInfor.append("table.length = "); 7 tableInfor.append(table.length); 8 tableInfor.append("\n"); 9 tableInfor.append("table node:"); 10 tableInfor.append("\n"); 11 for(Entry enty:table){ 12 tableInfor.append("entry :{"); 13 tableInfor.append(enty); 14 tableInfor.append("}"); 15 if(enty!=null)//if enty is null,the table didn't add any value in this position 16 while(enty.next!=null){ 17 tableInfor.append("-->{"); 18 tableInfor.append(enty.next); 19 tableInfor.append("}");
enty=enty.next; 20 } 21 tableInfor.append("\n"); 22 } 23 tableInfor.append("===========end==============="); 24 return tableInfor.toString(); 25 }
就是迭代打印出table數組對象,和對象的鏈的所有值,用於監控table值的變化
二,編譯並覆蓋rt.jar,實現對JDK代碼的修改
修改hashmap 類后,重新編譯HashMap類,JDK編譯器 javac HashMap –Xlint:unchecked
找到生成的hashmap的classes
把classes copy並替換進 JDK 的JRE\jrt.ar包中
然后啟動eclipse,運行測試,注意你eclipse中配置的JDK要和你修改的一致
三,簡單測試HashMap原理
1,HashMap的初始化
很明確. HashMap 初始化的時候就是長度為16的對象數組 (static final int DEFAULT_INITIAL_CAPACITY = 16;)
2,HashMap的擴容
根據hashmap中的加載因子定義 static final float DEFAULT_LOAD_FACTOR = 0.75f;
那么得出,如果有16*0.75即12個,那么table數組將會翻倍擴容,驗證下
可以看到當你put的元素超過了13個時,那么table將會進行擴容,並把原來的值copy進去,其實和ArrayList的實現原理一樣,只不過增加了地址定位,鏈等
3,HashMap的鏈
當key的hashcode相同,equas不同時,就會根據hashcode計算table索引的對應的鏈添加元素,所以設計了一個equas相同,但是equas不同的key類
完整代碼如下

package org.benson.test; public class Key { private char mapKey; @Override public int hashCode() { return "A".hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Key other = (Key) obj; if (mapKey != other.mapKey) return false; return true; } public Key(char mapKey) { this.mapKey=mapKey; } @Override public String toString() { // TODO Auto-generated method stub return String.valueOf(mapKey); } }
添加並打印key
package org.benson.test; import java.util.HashMap; public class Test4MapTable { /**@author BensonHe QQ 107966750 * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub HashMap hmp=new HashMap(); hmp.put(new Key('A'), "valueA"); hmp.put(new Key('B'), "valueB"); System.out.println(hmp.getTableChageInfor("befor")); } }
run,測試結果如下
========befor===start=============== table.length = 16 table node: entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} entry :{B=valueB}-->{A=valueA} entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} entry :{null} ===========end===============
注:
發現如果根據hashcode計算的index相同,但並非單指hashcode相同,當然hashcode相同計算的index肯定是一樣的,具體是在hashmap中實現計算.比如hashcode=18 和 hashcode=33的屬於同一個鏈中
引用個外部圖片表示
便生成了對應的鏈,而不會新占用table的index
4,原理應用
要求輸出valueA,實現key方法
import java.util.HashMap; public class Test4MapTable { /**@author BensonHe QQ 107966750 * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub HashMap hmp=new HashMap(); hmp.put('A', "valueA"); System.out.println(hmp.get(new Key('A'))); // System.out.println(hmp.getTableChageInfor("befor")); } }
其實很簡單,覆蓋下hashcode和equals方法
如下

package org.benson.test; public class Key { private char mapKey; @Override public int hashCode() { return String.valueOf(mapKey).hashCode(); } @Override public boolean equals(Object obj) { // if (this == obj) // return true; // if (obj == null) // return false; // if (getClass() != obj.getClass()) // return false; // Key other = (Key) obj; // if (mapKey != other.mapKey) // return false; return obj.equals(mapKey); } public Key(char mapKey) { this.mapKey=mapKey; } @Override public String toString() { // TODO Auto-generated method stub return String.valueOf(mapKey); } }
附上 失敗嘗試
從JDK源碼看到,hashmap的table變量的作用范圍是default,即同package可以調用,故嘗試繼承hashmap 並放在同一個package中
然后ruan,執行失敗
拋出
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.util
找到java.lang.ClassLoader.preDefineClass,打開源碼找到了原因:
原來所有以java.開頭的package都是被禁止使用的,然而hashmap類又是在java.util中,所以此方法不行
有問題歡迎交流,thanks