一,初調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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM