Java源碼 HashMap


 HashMap類

  https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html

  

  public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

  

  子類:LinkedHashMap,PrinterStateReasons

 

  一、介紹

  HashMap繼承了AbstractMap類,AbstractMap類實現Map接口中的一些方法,是為了最大限度地減少實現Map接口需要進行的工作量,提高代碼重用。  

  Cloneable接口可以理解為一個標記接口,它並沒有定義方法。當一個對象調用Object的clone方法時,如果這個對象沒有實現Cloneable接口,會引發CloneNotSupportedException異常。(關於對象的拷貝,有深拷貝和淺拷貝的知識)clone方法執行的是淺拷貝,這個接口就是指示可以使用Object的clone方法。

  

  HashMap類實現了Map接口中所有的“可選操作”,允許key和value為null,非同步。

  HashTable類與HashMap幾乎一致,區別在於HashTable不允許key和value為null,而且是同步的。

  HashMap無法保證其中鍵值對的順序,即它無法保證鍵值對的順序會一直保持不變。

 

  HashMap類為基本的get和put操作,提供了常數時間的性能。

  HashMap的集合視圖上的迭代器所需要的時間和(HashMap實例的容量加上鍵值對的數量)成正比。所以,當需要考慮迭代器的性能時,不要把HashMap實例的容量(capacity屬性)設置得太高,或者別把load factor設置得太低。

  如上提到的,一個HashMap實例有兩個最重要的參數影響着它的性能:初始的capacity值和load factor值。

  capacity是哈希表中桶的數量,它的初始值代表着HashMap實例創建時桶的數量。

  load factor,在這個哈希表實例進行容量自動增長前,用來度量這個哈希表“滿”的程度。

  當哈希表中中鍵值對條目的數量超過load-factor和當前capacity的乘積時,哈希表就會被重新映射(is rehashed),這意味着它內部的數據結構被重建了,結果便是“新”的哈希表擁有了兩倍的桶數。(像數組的自動擴容)

 

  通常,HashMap的默認load-factor是0.75,這在時間代價和空間代價提供了一個很好的平衡。

  load-factor過高會減少空間開銷,但會增加查找成本,這體現在類中的大部分操作中,包括get和put。

  因此,在設置初始的capacity值時,需要好好考慮HashMap中鍵值條目的數量以及負載因子,以盡可能減少重新散列的次數。如果初始capacity大於(最大入口數除以負載因子),哈希表就不會進行重新散列。

  

  如果你有比較多的鍵值對需要存進HashMap,最好在創建它時配置一個足夠大的capacity,比起容量不足然后進行重散列,這樣可以更有效地存儲映射。

  需要注意的是,如果多個key的hashcode()值相同,會降低哈希表的性能。為了改善這種情況,若key實現了Comparable接口,HashMap可能會利用key之間的比較順序(comparison order)來消除“並列”(break ties)。

 

  由於HashMap是不同步的,當多個線程並發訪問一個HashMap時,若其中至少一個線程對HashMap進行了結構性的修改,那么就必須在外部進行同步(it must be synchronized externally)。這通常是通過自然封裝了HashMap的對象上完成的,如果這樣的對象不存在,那么這個HashMap就應該通過Collections.synchronizedMap方法裝飾,並且最好在創建時就進行,以防止這個HashMap上有以外的非同步訪問:

Map m = Collections.synchronizedMap(new HashMap(...));

  (所謂的結構性修改(structural modification)是增加or刪除鍵值記錄的操作,如果僅僅是改變其中某個key關聯的value,不屬於結構性操作。)

 

  HashMap類的所有集合視圖的產生方法所返回的迭代器都具有“快速失效”屬性:當一個實例在迭代器被創建后被進行了結構性的修改(除了通過迭代器的remove方法),迭代器會拋出一個 ConcurrentModificationException。所以,面對並發修改,迭代器會明確而快速地失效,而不是仍然承擔着並不確定的行為的風險。

  需要注意的是,迭代器並不保證它的“快速失效”屬性,因為在存在非同步並發修改的情形下,不可能做出任何硬性保證。

  所以,迭代器的快速失效行為應該僅用於檢測錯誤。

 

  二、HashMap的嵌套類

  繼承自AbstractMap的嵌套類:AbstractMap.SimpleEntry<K,V>, AbstractMap.SimpleImmutableEntry<K,V>

  繼承自Map接口的嵌套類:Map.Entry<K,V>

  事實是,HashMap中有許多嵌套類:

  如圖:

  Node類實現了Map.Entry<K,V>接口,源碼描述為“Basic hash bin node, used for most entries”,即為存儲單條鍵值映射記錄的數據結構;

  TreeNode類是“Entry for Tree bins”,這個是在某些情況下可以用來替代Node的(when bins get too large, they are transformed into bins of TreeNodes),該類繼承自LinkedHashMap.Entry<K,V>。

  KeySet、Values、EntrySet的嵌套類和返回集合視圖的三個方法相關;

 

  本來不打算解析嵌套類的,但是它們和很多方法息息相關,所以干脆好好分析一波!

  1、HashMap的屬性

 

  如圖,基本可以通過名稱判斷相關屬性的作用。

  size,表示當前map中鍵值映射記錄的條數;

  帶treeify字段的屬性,好像是和Node轉換成TreeNode的操作有關的;

  threshold意思是門檻、開始,用於判斷map是否進行resize操作的標准。

  table,一個包含Node元素的數組,會在第一次使用時被初始化。這個數組的長度,總是整數2的冪。

  modCount,記錄這個map被結構性修改的次數;

 

  2、 static class Node<K,V> implements Map.Entry<K,V> 

  注意到,其中key為final,但是value不為final,關注下put方法中包含相同key時的處理方法。

  這里的hash暫時沒明白干嘛的;

  next是不是表明了,鍵值映射記錄的存儲方式是鏈表?

/**
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
     */
    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;
        }
    }

 

  三、構造器

  根據Java集合框架的規約,集合類都應該提供一個無參構造器和一個Collection or Map類型的單參構造器,前者用於構造空的集合,后者用於復制另一個集合。

  •   HashMap(),構造一個空的HashMap實例,默認capacity為16,默認load-factor是0.75。
  •   HashMap(Map<? extends K,? extends V> m),這里的初始capacity,滿足需求即可。
  •   HashMap(int initialCapacity)
  •   HashMap(int initialCapacity, float loadFactor)

 

  四、方法

  1、public int size()

  返回鍵map中值映射記錄的條數;

 

  2、public boolean isEmpty()

  如果map中還沒有存儲鍵值映射的記錄,返回true。

public boolean isEmpty() {
        return size == 0;
    }

 

  3、static final int hash(Object key)

  待會看看它的用法。

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

 

 

  4、public V get(Object key)

  返回指定key所映射的value值;若map不包含此key的映射,返回null。

  特殊的情況是,這個key所映射的value值恰好為null,這時就需要利用containKey()來區分這兩種情況了。

  

  5、public boolean containsKey(Object key)

  查詢map中是否包含指定的key

 

  6、public V put(K key, V value)

  關聯指定的key和value,即是他們稱為map中的鍵值對,可以通過key查到這個value。

  如果key存在,對value進行替換。

 

  7、public void putAll(Map<? extends K,? extends V> m)

  將指定map中的所有映射復制到此map。

  如果key已存在,則其值會被新的替換。

 

  8、public V remove(Object key)

  9、public void clear()

  清空map

 

  10、public boolean containsValue(Object value)

  判斷是否map中有一個或者多個key的映射值為指定的value。

 

  11、public Set<K> keySet()

  Returns a Set view of the keys contained in this map.

 

  12、public Collection<V> values()

  Returns a Collection view of the values contained in this map.

 

  13、public Set<Map.Entry<K,V>> entrySet()

  Returns a Set view of the mappings contained in this map

 

  14、public V putIfAbsent(K key, V value)

  If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.

 

  15、public boolean remove(Object key, Object value)

  

  16、public boolean replace(K key, V oldValue, V newValue)

  Replaces the entry for the specified key only if currently mapped to the specified value.

 


免責聲明!

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



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