1. 程式人生 > >HashMap(HashSet)的實現

HashMap(HashSet)的實現

0. HashMap(TreeMAP)、HashSet、HashTable 的關係

  • HashMap 的底層則維護著 Node<K, V>[] table; 一個一維陣列用於快速訪問(只在初次使用時進行初始化,當需要擴容時,When allocated, length is always a power of two.)

    static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;          // 此處的 hash 相當於 bucket
            final K key;
            V value;
            Node<K,V> next;          // 每個節點均連結著一個連結串列;

    在呼叫 get 方法返回該 key 對應的 value 時,先根據 key 對應的 hash 找到 bucket

    if ( (tab = table) != null && 
            (n = tab.length) > 0 && 
            (first = tab[(n - 1) & hash]) != null) 
    {
        if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null
    && key.equals(k)))) { return first; // 找到直接返回 } // 否則遍歷其連結串列 if ( (e = first.next) != null) { do { .... } while ((e = e.next) != null); } }
  • HashMap 在解決衝突時,採用的是衝突連結串列(Separate chaining with linked lists)的方式;

  • HashSet 與 HashMap 有著相同的實現,HashSet 底層維護著一個 HashMap 物件,通過介面卡模式是對 HashMap 的進一步封裝(限制)
  • HashMap實現了Map介面,允許放入null元素,除該類未實現同步外,其餘跟Hashtable大致相同
  • 與 TreeMap 相比,TreeMap 是有序的;

1. HashMap 的實現

  • 有兩個引數可以影響HashMap的效能:初始容量(inital capacity)和負載係數(load factor,也叫裝填因子)。初始容量指定了初始table的大小,負載係數用來指定自動擴容的臨界值。
    • 當entry的數量(size)超過capacity*load_factor時,容器將自動擴容並重新雜湊。
  • hashCode() 與 equals() 方法:
    • hashCode()方法決定了物件會被放到哪個bucket裡,當多個物件的雜湊值衝突時,equals()方法決定了這些物件是否是“同一個物件”

2. 雜湊(hash)的一些細節

如果 hashCode 是負數會怎樣?負索引可不是你想要的。因此,一個改進的雜湊公式會移出符號位(符號為同 0 相與),然後再用取模(即 %)運算子計算剩餘部分。

(123  & 0x7FFFFFFF) % 20 = 3
(456 & 0x7FFFFFFF) % 20 = 16

references