1. 程式人生 > >淺析java8中HashMap的結構

淺析java8中HashMap的結構

HashMap中涉及的部分資料結構

陣列 陣列儲存區間是連續的,佔用記憶體嚴重,故空間複雜的很大。但陣列的二分查詢時間複雜度小,為O(1);陣列的特點是:定址容易,插入和刪除困難

連結串列

連結串列儲存區間離散,佔用記憶體比較寬鬆,故空間複雜度很小,但時間複雜度很大,達O(N)。連結串列的特點是:定址困難,插入和刪除容易

雜湊表之拉鍊法

雜湊表(Hash table,也叫散列表),是根據關鍵碼值(Key-value)而直接進行訪問的資料結構。它通過把關鍵碼值對映到表中一個位置來訪問記錄,有點類似於陣列,並且能在O(1)(衝突情況另算)下查詢到元素。這個對映函式叫做雜湊函式,存放記錄的陣列叫做散列表
需要注意的是,在 Java 8 中如果 hash 值相同的 key 數量大於指定值(預設是8)時使用平衡樹來代替連結串列,這會將get()方法的效能從O(n)提高到O(logn)。 而拉鍊法其實是hashtable查詢中一種解決衝突的查詢方法,也是最常用來實現hashtable的一種方法。
用拉鍊法實現的hashtable還可以理解為一個連結串列的陣列,因為該結構就是通過陣列+連結串列的組合來實現的,陣列的每個元素中都存放一個連結串列的頭結點。如下圖所示:

拉鍊法的具體實現是:將所有關鍵字為同義詞的結點連結在同一個單鏈表中。若選定的散列表長度為m,則可將散列表定義為一個由m個頭指標組成的指標陣列t[0..m-1]。凡是雜湊地址為i的結點,均插入到以t為頭指標的單鏈表中。t中各分量的初值均應為空指標。在拉鍊法中,裝填因子α可以大於1,但一般均取α≤1。舉個栗子:

上圖是一個長度為16的陣列,每個元素儲存著一個連結串列的頭結點,元素儲存在陣列中的位置(index)是通過hash(key)%len獲得的,也就是用元素key的雜湊值對陣列長度取模得到的。例如陣列索引為12的這條記錄,12%16=12,28%16=12,108%16=12.140%16=12。所以12、28、108、140都儲存在陣列索引為12的位置。 HashMap其實也是一個線性的陣列,就如剛剛所言,他的實現可以理解為一個“連結串列的陣列”。而且HashMap裡面實現了一個Node作為一個基礎Bean,用來儲存HashMap中的內容。該Bean中重要的屬性有hash、key、value、next。部分原始碼如下:
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }