淺談Java中的Hashmap

達聞西發表於2019-01-19

Java中,有一種而且我們使用很頻繁的資料結構,叫做HashMap,其實準確的來說,這是雜湊表的一種衝突解決的實現,那麼什麼是雜湊表呢?這個概念在網上可以找到很多專業的回答,這裡我們就舉一個很簡單的例子來說明一下什麼是雜湊表

首先我們要明確的一點,就是雜湊表肯定是用來存取東西的,那麼如果我們要把西瓜蘋果草莓這三種東西按照重量存到某個地方,方便我們後面再拿來使用,這個該如何操作呢?首先我們可以拿一個彈簧,然後使用這個彈簧將這些東西按照重量彈出去,因為重量的不同,將會落在不同的區域,這樣就將這些東西“儲存”了起來。如果我們後面要使用這些東西的時候,只需要按照重量再去使用一次彈簧就可以了,這樣就能找到我們所需要找的。

這裡我們再明確幾個概念,這個例子中的幾種東西,就是我們要存放的資料,彈簧指的是hash演算法,因為這些資料的存放是無序的,所以這些概念綜合起來看,稱這種形式為雜湊表

還有一個問題,就是如果在這個雜湊表中有兩個資料通過hash演算法落到了同一個區域,那麼在這裡就稱之為衝突,或者叫hash衝突,在雜湊表中,解決衝突的方式有線性探測法平方探測法鏈式地址法。。。很多種方式,在這裡就不討論每一種解決方式的利弊,我們只是對於Java中的HashMap作為研究。

如果看過HashMap原始碼的朋友就會知道,在JavaHashMap是基於鏈式地址法來實現的,當然也有很多朋友可能不太喜歡看原始碼,好的,那麼我們就按照HashMap來動手寫一個簡單的實現。

我們先定義一個節點類。

public class Entry {
    
    private Integer key;
    
    private String value;
    
    private Entry next;
    
    public Entry(Integer key, String value, Entry next){
        this.key = key;
        this.value = value;
        this.next = next;
    }
}

然後我們在定義一個CopyHashMap類。

public class CopyHashMap {
    
    private final static int DEFAULT_CAPCITY = 11;
    
    private Entry[] arrEntry;
    
    public CopyHashMap(){
        arrEntry = new Entry[DEFAULT_CAPCITY];
    }
    
    public CopyHashMap(int size){
        arrEntry = new Entry[size];
    }
    
    private int hash(int key){
        return key % DEFAULT_CAPCITY;
    }
    
    public String get(int key){
        int h = hash(key);
        Entry entry = arrEntry[h];
        for (Entry e = entry; e != null; e = e.getNext()){
            if (e.getKey().equals(key)){
                return e.getValue();
            }
        }
        return null;
    }
    
    public void put(int key, String value){
        int h = hash(key);
        Entry entry = arrEntry[h];
        for (Entry e = entry; e != null; e = e.getNext()){
            if (e.getKey().equals(key)){
                e.setValue(value);
            }
        }
        arrEntry[h] = new Entry(key, value, arrEntry[h]);
    }
}

這裡只是簡單的寫了一個getput方法,程式碼很簡單,看一看應該都能明白,可以看到其底層實現還是依賴於一個陣列,這裡有一個hash的方法,這個就是我們剛開始例子中的彈簧,對於每一個key都要去計算對應的hash值是多少,然後才能確定存放在陣列中的哪個位置,所以能夠看出,雜湊表中的hash演算法的設計是非常重要的。

這段程式碼中還有一些東西沒有做,比如remove方法,還有rehash方法,如果你有興趣,那麼可以動手實現一下這兩個方法,rehash的過程在這裡順便提一下,簡單的說就是如果儲存列表不夠用的情況下,hash表會擴容,然後將裡面的資料重新hash一次,當然這個rehash會提前開始進行的,所以這裡我們可以觀察到一個現象就是如果雜湊表rehash一次,對程式的效能影響還是比較大的,對了,在JDK8中,HashMap也做了一些改進,鏈式儲存的時候,在其長度達到某一個定值,後面的儲存方式會轉為紅黑樹的形式,有興趣的話可以看一下原始碼。

HashMapJava中的使用很頻繁,很多人應該都懂其原理,可能也有很多人不太明白,希望這篇文章能讓大家初窺HashMap的門道,更深入的研究就只能大家自己探索了。

相關文章