HashMap的自定義實現


一、背景:          

HashMap到底是怎么實現的?
一對一對的存放,通過key找value;map的鍵不能重復;自己怎么實現呢?
 
代碼:
Wife.java  輔助類
package com.cy.collection;

public class Wife {
    String name;
    
    public Wife(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return "Wife [name=" + name + "]";
    }
}
View Code

Map.java:

package com.cy.collection;

/**
 * 自定義實現Map
 */
public class Map {
    private Entry[] arr = new Entry[1000];    //這里暫時不考慮擴容
    private int size;                        //這里都是private的,不暴露size屬性
    
    /**
     * 1.鍵不能重復,如果含有這個鍵,就替換value
     * @param key
     * @param value
     */
    public void put(Object key, Object value){
        for(int i=0; i<size; i++){
            if(arr[i].key.equals(key)){
                arr[i].value = value;
                return;
            }
        }
        arr[size++] = new Entry(key, value);
    }
    
    //根據key獲取
    public Object get(Object key){
        for(int i=0; i<size; i++){
            if(arr[i].key.equals(key)){
                return arr[i].value;
            }
        }
        return null;
    }
    
    //根據key刪除
    public boolean remove(Object key){
        boolean success = false;
        for(int i=0;i<size;i++){
            if(arr[i].key.equals(key)){
                success = true;
                remove(i);
            }
        }
        return success;
    }
    private void remove(int i){
        int numMoved = size - i - 1;
        if(numMoved>0){
            System.arraycopy(arr, i+1, arr, i, numMoved);
        }
        arr[--size] = null;        //Let gc do its work
    }
    
    //containsKey
    public boolean containsKey(Object key){
        for(int i=0; i<size; i++){
            if(arr[i].key.equals(key)){
                return true;
            }
        }
        return false;
    }
    
    //containsValue 同containsKey
    
    //size
    public int size(){
        return size;
    }
}

/**
 * 用來存放鍵值對的條目
 */
class Entry{
    Object key;
    Object value;
    
    public Entry(Object key, Object value) {
        super();
        this.key = key;
        this.value = value;
    }
}

Test.java測試代碼:

package com.cy.collection;

public class Test {

    public static void main(String[] args) {
        Map map = new Map();
        map.put("張三", new Wife("abc"));
        map.put("李四", new Wife("def"));
        map.put("王五", new Wife("ghi"));
        System.out.println(map.get("張三"));
        map.remove("李四");
        System.out.println(map.size());
        
        map.put("張三", new Wife("aaa"));
        System.out.println(map.get("張三"));
        System.out.println(map.containsKey("張三"));
    }

}

輸出:
Wife [name=abc]
2
Wife [name=aaa]
true
View Code

 

雖然說實現了,但是上面Map不完美的地方:
1.每次get(key)都要遍歷數組一次,效率很低;
 
有沒有什么辦法可以讓查詢的效率高起來?
 

 二、map改進,哈希算法實現,使用數組和鏈表    

能不能通過什么方法來提高查詢效率?避免像上面的map一樣循環遍歷?能不能有好的辦法一下子就命中目標。
思路:

 

1)假如arr數組是無窮大的,現在要將一個key放進數組,先計算key.hashCode(),將hashCode值就放在arr數組的這個對應下標的位置,
即arr[key.hashCode]這個位置,這個位置就存放Entry(key,value)。下次再要查找get(key)的時候,計算key的hashCode值,然后從數組中
找到arr[key.hashCode]不就快速定位,拿出來了嗎?
2)但是,數組不是無窮大的,現在能不能將key的hashCode進行轉化,轉化成一個合理的數,比如arr[1000],數組的下標就是0~1000,能不能將hashCode
轉化為0~1000的一個數,這樣就可以放到對應下標值的位置上啦。
3)怎么轉換?hashCode%1000,來取余數,余數的范圍就是0-999,要放的鍵值對就放在arr[余數];
但是余數極大可能會重復,怎么辦?
4)Map的底層實現是數組+鏈表,現在數組里面不存放Entry對象,而是存放鏈表,如果余數相同,就在鏈表的后面繼續添加;
get(key)的時候,就在這個數組的位置arr(key.hashCode)上,查找這個鏈表,在遍歷;
 
圖示:
 
 
代碼:
Map.java:
package com.cy.collection;

import java.util.LinkedList;

/**
 * 自定義實現Map升級版
 * 1.提高查詢的效率
 */
public class Map {
    private LinkedList[] arr = new LinkedList[1000];    //Map的底層結構就是:數組+鏈表
    private int size;            
    
    /**
     * 1.鍵不能重復,如果含有這個鍵,就替換value
     */
    public void put(Object key, Object value){
        Entry e = new Entry(key, value);
    
     int hash = key.hashCode();
     hash = hash<0?-hash:hash;
        int a = hash % arr.length;
        
        if(arr[a]==null){
            LinkedList list = new LinkedList();
            arr[a] = list;
            list.add(e);
        }else{
            LinkedList list = arr[a];
            for(int i=0; i<list.size(); i++){
                Entry en = (Entry) list.get(i);
                if(en.key.equals(key)){
                    en.value = value;            //鍵值重復,覆蓋value
                    return;
                }
            }
            list.add(e);
        }
        size++;
    }
    
    //根據key獲取值
    public Object get(Object key){
        int a = key.hashCode() % arr.length;
        if(arr[a]!=null){
            LinkedList list = arr[a];
            for(int i=0; i<list.size(); i++){
                Entry e = (Entry) list.get(i);
                if(e.key.equals(key)){
                    return e.value;
                }
            }
        }
        return null;
    }
    
    //size
    public int size(){
        return size;
    }
}

/**
 * 用來存放鍵值對的條目
 */
class Entry{
    Object key;
    Object value;
    
    public Entry(Object key, Object value) {
        super();
        this.key = key;
        this.value = value;
    }
}

Test.java

package com.cy.collection;

public class Test {

    public static void main(String[] args) {
        Map map = new Map();
        map.put("張三", new Wife("abc"));
        map.put("李四", new Wife("def"));
        map.put("張三", new Wife("ghi"));
        System.out.println(map.get("張三"));
        System.out.println(map.size());
    }
    
    
}

輸出:
Wife [name=ghi]
2
View Code
 
 三、小結          
1.哈希算法的本質:通過這個算法,可以快速的定位元素在數組中的存儲位置;
2.從上面代碼可以看到,如果兩個obj互相equals了,那么他們的hashCode必然相等。
3.Object類的hashCode方法為:public native int hashCode();沒有實現,native表示本地的,調用本地的一些資源,和操作系統相關的,hashCode默認的實現是根據內存地址進行計算的,native,跟操作系統相關的一種本地方法;
 
 


免責聲明!

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



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