起因:故尝试调试下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