淺談Java中的Hashmap

Java中,有一種並且咱們使用很頻繁的數據結構,叫作HashMap,其實準確的來講,這是散列表的一種衝突解決的實現,那麼什麼是散列表呢?這個概念在網上能夠找到不少專業的回答,這裏咱們就舉一個很簡單的例子來講明一下什麼是散列表算法

首先咱們要明確的一點,就是散列表確定是用來存取東西的,那麼若是咱們要把西瓜蘋果草莓這三種東西按照重量存到某個地方,方便咱們後面再拿來使用,這個該如何操做呢?首先咱們能夠拿一個彈簧,而後使用這個彈簧將這些東西按照重量彈出去,由於重量的不一樣,將會落在不一樣的區域,這樣就將這些東西「保存」了起來。若是咱們後面要使用這些東西的時候,只須要按照重量再去使用一次彈簧就能夠了,這樣就能找到咱們所須要找的。數組

這裏咱們再明確幾個概念,這個例子中的幾種東西,就是咱們要存放的數據,彈簧指的是hash算法,由於這些數據的存放是無序的,因此這些概念綜合起來看,稱這種形式爲散列表數據結構

還有一個問題,就是若是在這個散列表中有兩個數據經過hash算法落到了同一個區域,那麼在這裏就稱之爲衝突,或者叫hash衝突,在散列表中,解決衝突的方式有線性探測法平方探測法鏈式地址法。。。不少種方式,在這裏就不討論每一種解決方式的利弊,咱們只是對於Java中的HashMap做爲研究。性能

若是看過HashMap源碼的朋友就會知道,在JavaHashMap是基於鏈式地址法來實現的,固然也有不少朋友可能不太喜歡看源碼,好的,那麼咱們就按照HashMap來動手寫一個簡單的實現。this

咱們先定義一個節點類。設計

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類。code

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算法的設計是很是重要的。rem

這段代碼中還有一些東西沒有作,好比remove方法,還有rehash方法,若是你有興趣,那麼能夠動手實現一下這兩個方法,rehash的過程在這裏順便提一下,簡單的說就是若是存儲列表不夠用的狀況下,hash表會擴容,而後將裏面的數據從新hash一次,固然這個rehash會提早開始進行的,因此這裏咱們能夠觀察到一個現象就是若是散列表rehash一次,對程序的性能影響仍是比較大的,對了,在JDK8中,HashMap也作了一些改進,鏈式存儲的時候,在其長度達到某一個定值,後面的存儲方式會轉爲紅黑樹的形式,有興趣的話能夠看一下源碼。get

HashMapJava中的使用很頻繁,不少人應該都懂其原理,可能也有不少人不太明白,但願這篇文章能讓你們初窺HashMap的門道,更深刻的研究就只能你們本身探索了。源碼

相關文章
相關標籤/搜索