一,初调HashMap,如何修改JDK的源码进行调试 【深入JDK源码】

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

image

找到生成的hashmap的classes

image

把classes copy并替换进 JDK 的JRE\jrt.ar包中

image

然后启动eclipse,运行测试,注意你eclipse中配置的JDK要和你修改的一致

三,简单测试HashMap原理

1,HashMap的初始化

image

很明确. 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数组将会翻倍扩容,验证下

image

可以看到当你put的元素超过了13个时,那么table将会进行扩容,并把原来的值copy进去,其实和ArrayList的实现原理一样,只不过增加了地址定位,链等

3,HashMap的链

当key的hashcode相同,equas不同时,就会根据hashcode计算table索引的对应的链添加元素,所以设计了一个equas相同,但是equas不同的key类

 完整代码如下

View Code
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方法

如下

View Code
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中

image

然后ruan,执行失败

抛出

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.util

image

找到java.lang.ClassLoader.preDefineClass,打开源码找到了原因:

image

原来所有以java.开头的package都是被禁止使用的,然而hashmap类又是在java.util中,所以此方法不行

有问题欢迎交流,thanks

原文地址:https://www.cnblogs.com/springsource/p/2832838.html