集合-HashMap解析

一、概述

  1. HashMap的底層數據結構是數組,但是數組中存放的并不是一個對象而是鏈表。所以也可以成HashMap的數據結構是哈希桶。在JDK1.8中如果鏈表存放的元素超過8個就將鏈表轉換成紅黑樹。為了保證元素沖突時,查詢和插入效率。Andro中則一直是鏈表。數組每一位存儲的是鏈表的頭部。鏈表的使用時基于哈希表解決沖突(碰撞)使用的方法:鏈地址法
  2. HashMap默認數組長度(Android:2然后在第一次增加數據后就變成了4,Java:16),默認擴容長度為原來數組長度的一半,數組長度也稱容量。在數組內元素超過閾時,就會進行擴容。閾=負載系數數組長度。負載系數可以自定義也可以使用默認值0.75*;
  3. 關于hash計算:通過使用key的hashCode(),然后經過哈希函數將hashCode轉換為相應哈希值,通過位運算來計算應該放在表中的哪個位置,位運算效率高于取模運算。哈希函數盡量使得數據的分布均勻。HashMap的Key對象盡量需要復寫父類的hashCode()和equals()方法,這兩個方法主要用于HashMap中存儲。
    其中hashCode用于hash值及表中位置的位運算。equals用于判斷key值是否相等。
  4. 關于擴容以及擴容后的數據位置轉換。首先判斷是否需要擴容。需要擴容,那么容量變為原來的兩倍。擴容后數據需要判斷是否需要向數組的高位移動,判斷方式是元素的hash值與上數組原來的長度(hash&oldCap),如果不為0就需要向高位移動。高位的位置是現在的位置加上oldCap(原來的數組長度)。
  5. HashMap中重要的兩個參數是容量(Capacity)和負載系數(Load factor)。其中容量是指桶的數目,負載系數是在桶填滿程度的最大比例。這兩個參數關系到了哈希表的性能。Android中負載因子沒有用。。。

二、構造函數及節點信息

2.1 構造函數

關鍵字transient表示實現接口Serializable的對象字段不能被自動序列化

Java

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//默認初始化容量,16
static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量:2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默認負載系數:0.75
transient Node<K,V>[] table;//表,Node數組
transient Set<Map.Entry<K,V>> entrySet;//Map的Entry
transient int size;//大小
transient int modCount;//修改次數
int threshold;//閾,負載系數*容量
final float loadFactor;//負載系數,可自定義
//自定義初始化容量和負載系數的構造函數
public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)//如果初始化容量小于0,拋出異常
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)//如果初始化容量大于最大容量,那么將初始化容量設為最大容量
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))//負載系數小于0或者是空
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;//設置負載系數
        this.threshold = tableSizeFor(initialCapacity);//設置閾,在第一次put的時候將表的容量設為當前的閾值,并重新修改閾值
}
//只有初始化容量的構造函數
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//默認構造函數,只是將負載因子設為默認參數
public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//參數是Map的構造參數
public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;//設置負載因子,為默認因子
        putMapEntries(m, false);//將map的數據插入進去
}
//將cap的所有除最高位外變為1,然后+1,這樣就變成了大于等于cap的2的n次方
static final int tableSizeFor(int cap) {
        int n = cap - 1;//減1,方便處理最高位的值,假定最高位為m,最高位指的是int的二進制表示最高(第一個)1出現的位置,如果這個值的二進制是m位為1,而其他位為0的情況,類似與(10000=16),就是2的n次方。
        n |= n >>> 1;//將m-1位置處理為1
        n |= n >>> 2;//將m-1到m-4位置處理為1
        n |= n >>> 4;//將m-1到m-8位置處理為1
        n |= n >>> 8;//將m-1到m-16位置處理為1
        n |= n >>> 16;//將m-1到m-32位置處理為1
        //其實就是將最高位外全部處理為1,具體參考下圖演示的0,15,16,17
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
位運算

Android

private static final int MINIMUM_CAPACITY = 4;//最小的數組長度
private static final int MAXIMUM_CAPACITY = 1 << 30;
private static final Entry[] EMPTY_TABLE
            = new HashMapEntry[MINIMUM_CAPACITY >>> 1];//空數組,長度為2
static final float DEFAULT_LOAD_FACTOR = .75F;//默認的負載因子
transient HashMapEntry<K, V>[] table;//表,使用HashMapEntry的表
transient HashMapEntry<K, V> entryForNullKey;//專門存儲null鍵的鍵值對對象
transient int size;//size
transient int modCount;//修改次數
private transient int threshold;//閾
private transient Set<K> keySet;//key的集合
private transient Set<Entry<K, V>> entrySet;//entry的集合
private transient Collection<V> values;//值的集合

public HashMap() {//默認構造函數
        table = (HashMapEntry<K, V>[]) EMPTY_TABLE;//將table設置空數組
        //閾,在第一次增加元素的時候會進行數組擴容,數組長度也就變成了4
        threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
}
//設置初始容量的構造函數
public HashMap(int capacity) {
        if (capacity < 0) {//如果容量小于0,拋出異常
            throw new IllegalArgumentException("Capacity: " + capacity);
        }

        if (capacity == 0) {//如果容量為0
            @SuppressWarnings("unchecked")
            HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE;
            table = tab;//將table設為空數組
            threshold = -1; // Forces first put() to replace EMPTY_TABLE,閾值設為-1,為了第一次增加數據時擴容
            return;
        }
        //容量小于最小容量,就將容量設置最小容量
        if (capacity < MINIMUM_CAPACITY) {
            capacity = MINIMUM_CAPACITY;
        } else if (capacity > MAXIMUM_CAPACITY) {//容量大于最大容量,就設為最大容量
            capacity = MAXIMUM_CAPACITY;
        } else {//變成2的n次方
            capacity = Collections.roundUpToPowerOfTwo(capacity);
        }
        //創建數組,閾值為數組的0.75
        makeTable(capacity);
}
//設置初始容量,負載因子沒有用
public HashMap(int capacity, float loadFactor) {
        this(capacity);//調用容量函數,負載因子并沒有用,只是為了符合java的構造方法
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new IllegalArgumentException("Load factor: " + loadFactor);
        }
}
//Map的數組
public HashMap(Map<? extends K, ? extends V> map) {
        this(capacityForInitSize(map.size()));//調用容量相關的構造函數
        constructorPutAll(map);
}
//使用容量,制造數組
private HashMapEntry<K, V>[] makeTable(int newCapacity) {
        @SuppressWarnings("unchecked") 
      HashMapEntry<K, V>[] newTable
                = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity];
        table = newTable;//將數組設為新創建的數組
        //調整閾值
        threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
        return newTable;
}
static int capacityForInitSize(int size) {
         //將size擴容一半
        int result = (size >> 1) + size; // Multiply by 3/2 to allow for growth
        //~(MAXIMUM_CAPACITY-1)=1<<31 + 1<<30
        // (result & ~(MAXIMUM_CAPACITY-1))==0 ,true表示小于1<<30
        // boolean expr is equivalent to result >= 0 && result<MAXIMUM_CAPACITY
        return (result & ~(MAXIMUM_CAPACITY-1))==0 ? result : MAXIMUM_CAPACITY;
}

Collections.java

//具體的算法,和Java中相同
public static int roundUpToPowerOfTwo(int i) {
        i--; // If input is a power of two, shift its high-order bit right.
        // "Smear" the high-order bit all the way to the right.
        i |= i >>>  1;
        i |= i >>>  2;
        i |= i >>>  4;
        i |= i >>>  8;
        i |= i >>> 16;
        return i + 1;
}

2.2 節點對象

Java

Java在1.8中有兩種Node節點:普通Node(用于桶的鏈表)和樹的Node(用于紅黑樹節點)
TreeNode的節點繼承自普通Node(在JDK1.8中實際是孫子)。
普通Node

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;//哈希值
        final K key;//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;
        }
        //獲取Key
        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的鍵值對
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;//將對象轉換成相應的對象
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;//key和值相等,鍵值對相等。
            }
            return false;
        }
}

紅黑樹的Node
這個紅黑樹使用Hash值進行比較,如果Hash相同,那么再使用key值進行比較。key值需要比較功能。
紅黑樹的增加刪除需要平衡樹,關于紅黑樹,參考鏈接內的內容。

紅黑樹的根節點放入哈希表中。

關于節點為啥不講紅黑樹節點:因為也沒太看懂Java代碼。這里的紅黑樹表現會比較復雜。

TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
}

Android

HashMap條目,鏈表的節點

static class HashMapEntry<K, V> implements Entry<K, V> {
        final K key;
        V value;
        final int hash;//哈希值
        HashMapEntry<K, V> next;//下一個節點

        HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
        }
        public final K getKey() {//獲取Key
            return key;
        }
        public final V getValue() {//獲取值
            return value;
        }
        public final V setValue(V value) {//設置值
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
        @Override 
      public final boolean equals(Object o) {//判斷是否相等
            if (!(o instanceof Entry)) {
                return false;
            }
            Entry<?, ?> e = (Entry<?, ?>) o;//key和value相等,那么Entry相等
            return Objects.equal(e.getKey(), key)
                    && Objects.equal(e.getValue(), value);
        }
        @Override public final int hashCode() {//哈希值
            return (key == null ? 0 : key.hashCode()) ^
                    (value == null ? 0 : value.hashCode());
        }
        @Override public final String toString() {//轉換成字符串
            return key + "=" + value;
        }
}

三、增加元素

3.1 增一個元素:增加一個鍵值對

將一個元素放入哈希表

Java

public V put(K key, V value) {
        //根據key的hashCode(),求出key的哈希值。
        return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //如果tab是空,或者tab的長度為0,需要擴容
       //默認構造函數創建的table就是空
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;//tab重新設置大小,并將長度賦值
        if ((p = tab[i = (n - 1) & hash]) == null)//計算key對應hash位置是否為空
            tab[i] = newNode(hash, key, value, null);//如果為空表示,這個位置沒有值,直接設置新值即可。
        else {
            Node<K,V> e; K k;//
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;//哈希值、key相等表示放入的鍵值對需要修改
            else if (p instanceof TreeNode)//如果是樹節點,就采用紅黑樹的插入方法
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {//鏈表的插入方法
                for (int binCount = 0; ; ++binCount) {//循環鏈表
                    if ((e = p.next) == null) {//將p向后移動,直到p.next空
                        p.next = newNode(hash, key, value, null);//將p的后節點指向新創建的節點
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);//如果count=7,表示插入之后達到了8個,將數據結構換成鏈表
                        break;
                    }//如果值相等,打斷循環
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }//如果e不為空,表示找到了e,將e的值修改掉。
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;//增加修改次數
        if (++size > threshold)//如果size大于閾值,需要進行擴容。
            resize();
        afterNodeInsertion(evict);
        return null;
}
final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;//老table
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//老的容量
        int oldThr = threshold;//老的閾值
        int newCap, newThr = 0;//新的長度和新的閾值
        if (oldCap > 0) {//如果老表的長度大于0
            if (oldCap >= MAXIMUM_CAPACITY) {//如果老的容量大于設定的最大容量
                threshold = Integer.MAX_VALUE;//將閾值設為Integer的最大值,2的31次方-1;
                return oldTab;//返回老表
            }//新容量=老容量*2,如果新容量小于最大容量值并且老容量大于等于默認容量值
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold,將閾值double之后直接設為新閾值
        }//如果老閾值大于零,并且老的容量小于0,將初始容量設為閾值
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;//構造函數使用設置初始容量相關的函數
        else {               // zero initial threshold signifies using defaults
            //默認構造函數,第一執行put操作的時候。
            newCap = DEFAULT_INITIAL_CAPACITY;//將容量設置為初始容量
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//新的閾值為默認的負載因子*容量
        }
        if (newThr == 0) {//如果新的閾值為0,重新設定新閾值
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;//將閾值賦值
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//創建新的數組
        table = newTab;//將table指向新創建的數組
        if (oldTab != null) {//如果老數組不為空
            for (int j = 0; j < oldCap; ++j) {//循環遍歷數組
                Node<K,V> e;//臨時記錄節點
                if ((e = oldTab[j]) != null) {//記錄數組中相應位置的節點,并判斷是否為空,如果為空,直接跳過。不為空才進行處理
                    oldTab[j] = null;//將老數組中相應位置元素置空
                    if (e.next == null)//如果e沒有下一個元素,表示這個數組內放置的只有一個元素
                        newTab[e.hash & (newCap - 1)] = e;//將元素移動到新數組對應位置即可
                    else if (e instanceof TreeNode)//如果節點是樹,那么久使用紅黑樹的拆分
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order,鏈表的拆分
                        Node<K,V> loHead = null, loTail = null;//低位區,頭尾“指針”
                        Node<K,V> hiHead = null, hiTail = null;//高位區,頭尾“指針”
                        Node<K,V> next;//下一個
                        do {
                            next = e.next;//將next后移,然后處理e
                            if ((e.hash & oldCap) == 0) {//如果計算結果值為0,表示處于低位
                                if (loTail == null)//沒有尾指針
                                    loHead = e;//將頭部指針指向e
                                else//將尾部的下一個指向e
                                    loTail.next = e;
                                loTail = e;//將尾部指針指向e
                            }
                            else {//如果計算結果不為0,表示處于高位
                                if (hiTail == null)//高位頭指針為空
                                    hiHead = e;//高位頭指針指向e
                                else//高位頭指針不為空
                                    hiTail.next = e;//高位尾指針下一個指向e
                                hiTail = e;//高位尾指針移向e
                            }
                        } while ((e = next) != null);//將e重新指向空指針
                        if (loTail != null) {//低位尾指針不為空
                            loTail.next = null;//將尾指針下一位置空
                            newTab[j] = loHead;//將對應新數組位置設置為低位頭指針
                        }
                        if (hiTail != null) {//如果高位尾指針不為空
                            hiTail.next = null;//將高位尾指針下一位置空
                            newTab[j + oldCap] = hiHead;//將新數組高位對應位置指向高位尾指針。
                        }
                    }
                }
            }
        }//如果為空,直接返回新創建的數組
        return newTab;
}

static final int hash(Object key) {
        int h;
        //如果key不為null,將key的hashCode亦或上key的HashCode
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

 Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next);
}

JDK1.8的相關代碼

HashMap<Integer, Integer> hp = new HashMap<>();
hp.put(0, 0);
hp.put(16, 16);
hp.put(32, 32);//0,16,32,在沒有擴容前,都放在0位置上
hp.put(1, 1);
hp.put(31, 16);
hp.put(2, 2);
hp.put(3, 3);
hp.put(4, 4);
hp.put(5, 5);
hp.put(6, 6);
hp.put(7, 7);
hp.put(8, 8);//此時size是12 ,容量是16,在插入9之后進行擴容
hp.put(9, 9);//在擴容后,16因為需要變化需要重新換位置,換到了高位上。

關于容量從16變化到32
判斷是否需要重新換位置的標準是(hash&新容量-1)是否可以在原來的位置上。
但是上面的判斷變化非常復雜,從而采用了一種簡單的變化標準,就是下圖中的變化。
其實判斷的核心標準在于新增的高位變化了1,就是從原來的判斷4位到判斷5位。就是最高位的判斷。其中判斷新變化的第5位是否是1即可,因為是1表示需要移動位置。因為容量變為32之后,位置考慮的也只是hash值后5位。所以hash&oldCap!=0就是表示需要換位的相關元素,將它們拼成新的鏈表

關于新位置為什么使用[j + oldCap],是因為只變化了最高位,這個最高位為1低位全為0在二進制上表示就oldCap

哈希表中鏈表擴容相關位運算

Android

使用了一個固定的節點對象,來存儲null鍵的情況。
沒有特別復雜的算法,一切都是看起來那么簡單。。。

@Override public V put(K key, V value) {
        if (key == null) {//插入空鍵
            return putValueForNullKey(value);
        }
        //計算Hash值,這個hash值的計算需要
        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        int index = hash & (tab.length - 1);//計算表中的位置
        for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {//判斷這位置是否有數據,如果沒有向后移動
            if (e.hash == hash && key.equals(e.key)) {//找到hash值相等和key相等的數據
                preModify(e);//預修改數據
                V oldValue = e.value;//找到了,修改數據
                e.value = value;
                return oldValue;
            }
        }
        // No entry for (non-null) key is present; create one
        modCount++;
        if (size++ > threshold) {//如果size增加后,大于了閾值
            tab = doubleCapacity();//容量擴容
            index = hash & (tab.length - 1);//修改位置
        }
        addNewEntry(key, value, hash, index);//新增加鍵值對
        return null;
    }

    private V putValueForNullKey(V value) {
        HashMapEntry<K, V> entry = entryForNullKey;//空鍵節點
        if (entry == null) {//如果不存在空鍵節點,創建一個
            addNewEntryForNullKey(value);
            size++;
            modCount++;
            return null;
        } else {//如果存在空鍵節點,修改空鍵節點
            preModify(entry);//預修改,主要針對LinkeHash修改的方法,在HashMap中是空方法。
            V oldValue = entry.value;//修改值
            entry.value = value;
            return oldValue;
        }
    }
  void addNewEntryForNullKey(V value) {
        //創建一個空鍵的HashMapEntry
        entryForNullKey = new HashMapEntry<K, V>(null, value, 0, null);
    }
void preModify(HashMapEntry<K, V> e) { }
//在相應的位置創建一個新的條目即可
void addNewEntry(K key, V value, int hash, int index) {
        table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]);
}
//擴容,雙倍擴容
private HashMapEntry<K, V>[] doubleCapacity() {
        HashMapEntry<K, V>[] oldTable = table;//老表
        int oldCapacity = oldTable.length;//老容量
        if (oldCapacity == MAXIMUM_CAPACITY) {//如果老的容量已經等于最大值就返回老表
            return oldTable;
        }
        int newCapacity = oldCapacity * 2;//新容量=老容量擴大2倍,因為初始容量都是2的倍數,最大容量也是2的倍數,所以擴容后不會超過
        HashMapEntry<K, V>[] newTable = makeTable(newCapacity);//確保新表容量是2的倍數
        if (size == 0) {//如果size=0,不需要處理表中的數據
            return newTable;//直接將新表返回
        }
        for (int j = 0; j < oldCapacity; j++) {//遍歷老表
            /*
             * Rehash the bucket using the minimum number of field writes.
             * This is the most subtle and delicate code in the class.
             */
            HashMapEntry<K, V> e = oldTable[j];//記錄位置的數據
            if (e == null) {//如果數據為空,繼續
                continue;
            }
            int highBit = e.hash & oldCapacity;//鏈表首位數據高位的bit,其他位全是0
            HashMapEntry<K, V> broken = null;//相等于尾節點
            newTable[j | highBit] = e;// j | highBit等于j+highBit,因為j和highBit的位值有差異
            //循環鏈表
            for (HashMapEntry<K, V> n = e.next; n != null; e = n, n = n.next) {
                int nextHighBit = n.hash & oldCapacity;//計算鏈表其他數據的bit
                if (nextHighBit != highBit) {//如果不相等,表示需要移動位置
                    if (broken == null)//如果broken為空,表示沒有移動過
                        newTable[j | nextHighBit] = n;//直接移動數據即可
                    else//移動過數據,就將broken的后位置為n即可
                        broken.next = n;
                    broken = e;//將broken指向e,具體參考下圖的演示
                    highBit = nextHighBit;//將高位置移動
                }
            }
            if (broken != null)//如果broken不為空,將broken的后續節點指向空
                broken.next = null;
        }
        return newTable;
    }

Collections.java

public static int secondaryHash(Object key) {
        return secondaryHash(key.hashCode());
}
private static int secondaryHash(int h) {
        // Spread bits to regularize both segment and index locations,
        // using variant of single-word Wang/Jenkins hash.
        h += (h <<  15) ^ 0xffffcd7d;
      //-12931
      //1111 1111 1111 1111 1100 1101 0111 1101
        h ^= (h >>> 10);
        h += (h <<   3);
        h ^= (h >>>  6);
        h += (h <<   2) + (h << 14);
        return h ^ (h >>> 16);
}
Android中擴容時鏈表處理

3.2 增加Map中的元素

Java

public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {//map中的數據大于0個
            if (table == null) { // pre-size,主要針對空數組預加載閾值
                float ft = ((float)s / loadFactor) + 1.0F;//這個值是map種的容量
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);//設定容量
                if (t > threshold)//如果map中容量超過閾值,修改閾值
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)//如果容量查過閾值,那么修改擴容
                resize();
            //遍歷,并將map中數據插入到HashMap中
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

Android

循環一個個增加元素

 @Override public void putAll(Map<? extends K, ? extends V> map) {
        ensureCapacity(map.size());//確保容量足夠
        super.putAll(map);//調用父類的putAll方法
    }
private void ensureCapacity(int numMappings) {
        int newCapacity = Collections.roundUpToPowerOfTwo(capacityForInitSize(numMappings));//將容量擴容
        HashMapEntry<K, V>[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (newCapacity <= oldCapacity) {//確保需要的容量小于已有容量,直接返回
            return;
        }
        if (newCapacity == oldCapacity * 2) {//如果新容量是老容量的2倍
            doubleCapacity();//直接擴容,然后返回
            return;
        }

        // We're growing by at least 4x, rehash in the obvious way
        HashMapEntry<K, V>[] newTable = makeTable(newCapacity);
       //新創建數組
        if (size != 0) {
            int newMask = newCapacity - 1;//新容量-1
            for (int i = 0; i < oldCapacity; i++) {
                for (HashMapEntry<K, V> e = oldTable[i]; e != null;) {
                    HashMapEntry<K, V> oldNext = e.next;//記錄e的后節點
                    int newIndex = e.hash & newMask;//計算新位置
                    HashMapEntry<K, V> newNext = newTable[newIndex];//記錄新位置的數據
                    newTable[newIndex] = e;//將新位置的數據設為e
                    e.next = newNext;//將e后繼指向原來新位置的數據
                    e = oldNext;//將e指向原來e的后繼,循環鏈表
                }
            }
        }
    }

AbstractMap.java

public void putAll(Map<? extends K, ? extends V> map) {
        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
}

四、刪除元素

4.1刪除Key相等的元素

在key相等的情況下就刪除元素,默認的刪除元素的方法
還有一個刪除key相等和Value相等的元素方法

public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
}

public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
}
//哈希值、鍵、值、是否需要值相等、移動的(用于紅黑樹)?
final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        //表,相應位置p節點,表長度,位置
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        //表不為空,表長度大于0,表相應位置元素不為空
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            //需要刪除的節點
            Node<K,V> node = null, e; K k; V v;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;//hash值相等和key相等,表示找到了元素
            else if ((e = p.next) != null) {//沒有直接找到元素,處理鏈表或紅黑樹
                if (p instanceof TreeNode)//處理紅黑樹,找到節點
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {//處理鏈表
                    do {//找到hash和key相等的記錄為node
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;//刪除位置前的元素
                    } while ((e = e.next) != null);
                }
            }
            //判斷是否可以刪除
           // 節點不為空,是否需要值相等
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)//刪除樹的節點
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p)//如果刪除的是鏈表的頭節點
                    tab[index] = node.next;//將數組指向鏈表的下一位置即可
                else//將前節點的后繼指向刪除節點的后繼
                    p.next = node.next;
                ++modCount;//修改次數增加
                --size;//size減小
                afterNodeRemoval(node);//LinkeHashMap使用
                return node;
            }
        }
        return null;
}

Android

@Override public V remove(Object key) {
        if (key == null) {//如果key等于null,調用null的刪除方法
            return removeNullKey();//移除空元素
        }
        int hash = Collections.secondaryHash(key);//計算Hash值
        HashMapEntry<K, V>[] tab = table;//表
        int index = hash & (tab.length - 1);//計算位置
        //循環鏈表,記錄節點的前節點
        for (HashMapEntry<K, V> e = tab[index], prev = null;
                e != null; prev = e, e = e.next) {
            //hash值相等和key相等,表示找到了元素,
            if (e.hash == hash && key.equals(e.key)) {
                if (prev == null) {//如果沒有前節點,表示需要刪除的鏈表的尾節點
                    tab[index] = e.next;
                } else {//如果有將前節點的后節點指向新的后節點
                    prev.next = e.next;
                }
                modCount++;//修改次數增加
                size--;//size-1
                postRemove(e);//預刪除
                return e.value;
            }
        }
        return null;
    }
private V removeNullKey() {
        HashMapEntry<K, V> e = entryForNullKey;
        if (e == null) {//如果e為null,表示沒有存儲空鍵值的元素
            return null;
        }
        entryForNullKey = null;//將空元素置為空
        modCount++;//修改次數
        size--;//size減小
        postRemove(e);//預刪除,子類可以能有相應取消連接的實現
        return e.value;
}
void postRemove(HashMapEntry<K, V> e) { }

五、修改元素

Java

除put外還有replace相關方法,

@Override
public V replace(K key, V value) {
        Node<K,V> e;
        //找到節點病替換節點的值
        if ((e = getNode(hash(key), key)) != null) {
            V oldValue = e.value;
            e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
        return null;
}

@Override
public boolean replace(K key, V oldValue, V newValue) {
        Node<K,V> e; V v;
        //找到key和value都相等的節點,替換這個節點的值
        if ((e = getNode(hash(key), key)) != null &&
            ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
            e.value = newValue;
            afterNodeAccess(e);
            return true;
        }
        return false;
}

@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Node<K,V>[] tab;
        if (function == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            //循環遍歷數組,處理數組中的數據
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    e.value = function.apply(e.key, e.value);
                }
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
}

Android

沒有replace相關的API

六、查詢元素

Java

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
}
 @Override
public V getOrDefault(Object key, V defaultValue) {
        Node<K,V> e;//如果查找的結果為空,就返回給定的默認值
        return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //找到hash對相應位置的元素
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //檢查第一個節點key是否相等
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {//是否有后繼節點
                if (first instanceof TreeNode)//如果是樹形結構,調用樹形結構的獲取方法
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {//循環遍歷鏈表,直到找到相應的節點
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
}

Android

除了紅黑樹外與Java沒有特殊差異

public V get(Object key) {
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }

        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;
            }
        }
        return null;
}

七、其他

7.1 包含元素

Java

//包含Key
public boolean containsKey(Object key) {
        return getNode(hash(key), key) != null;
}
//是否包含值
public boolean containsValue(Object value) {
        Node<K,V>[] tab; V v;
        if ((tab = table) != null && size > 0) {
            //循環遍歷數組、鏈表以及紅黑樹
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    if ((v = e.value) == value ||
                        (value != null && value.equals(v)))
                        return true;
                }
            }
        }
        return false;
}

Android

@Override 
public boolean containsKey(Object key) {
        if (key == null) {
            return entryForNullKey != null;
        }
        //計算hash值
        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        //遍歷tab對應的鏈表
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return true;
            }
        }
        return false;
}
@Override 
public boolean containsValue(Object value) {
        HashMapEntry[] tab = table;
        int len = tab.length;
        if (value == null) {//如果值為空,先遍歷數組中是否有null值,如果有返回true,如果沒有查詢空鍵值對中是否包含值
            for (int i = 0; i < len; i++) {
                for (HashMapEntry e = tab[i]; e != null; e = e.next) {
                    if (e.value == null) {
                        return true;
                    }
                }
            }
            return entryForNullKey != null && entryForNullKey.value == null;
        }
        // value is non-null,循環遍歷判斷是否包含相應的元素
        for (int i = 0; i < len; i++) {
            for (HashMapEntry e = tab[i]; e != null; e = e.next) {
                if (value.equals(e.value)) {
                    return true;
                }
            }
        }
        //最后判斷空鍵值對的值是否是需要查找的值
        return entryForNullKey != null && value.equals(entryForNullKey.value);
}

7.2 集合的Size

Java

public int size() {
        return size;
}

Android

@Override public int size() {
        return size;
    }

7.3 集合是否為空

Java

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

Android

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

7.4 清空

Java

循環變量數組將數組元素置為空,紅黑樹和鏈表等待JVM自動回收

public void clear() {
        Node<K,V>[] tab;
        modCount++;
        if ((tab = table) != null && size > 0) {
            size = 0;
            for (int i = 0; i < tab.length; ++i)
                tab[i] = null;
        }
}

Android

使用了工具類,將數組及相應鏈表內的數據全部填充為空

@Override 
public void clear() {
        if (size != 0) {
            Arrays.fill(table, null);
            entryForNullKey = null;
            modCount++;
            size = 0;
        }
}

八、遍歷

8.1 鍵值對迭代器

Java

對應EntrySet的迭代器

//鍵值對Set
public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
//鍵值對Set繼承了抽象的Set
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public final int size()                 { return size; }//返回HashMap的大小
        public final void clear()               { HashMap.this.clear(); }//清空數據
        public final Iterator<Map.Entry<K,V>> iterator() {//迭代器
            return new EntryIterator();
        }
        public final boolean contains(Object o) {//是否包含對象
            if (!(o instanceof Map.Entry))//判斷是否是鍵值對類型
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;//轉換為對應的鍵值對
            Object key = e.getKey();//獲取Key
            Node<K,V> candidate = getNode(hash(key), key);//獲取節點的鍵值對
            return candidate != null && candidate.equals(e);//判斷節點是否相等
        }
        public final boolean remove(Object o) {//移除鍵值對
            if (o instanceof Map.Entry) {//如果是Map的鍵值對
                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
                Object key = e.getKey();
                Object value = e.getValue();
                //找到鍵和值相等的鍵值對,并移除它
                return removeNode(hash(key), key, value, true, true) != null;
            }
            return false;
        }
        //拆分哈希表
        public final Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
       public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
}
//Entry的鍵值對,繼承了默認鍵值對
final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
}

abstract class HashIterator {
        Node<K,V> next;        // next entry to return,下一個節點
        Node<K,V> current;     // current entry,當前節點,返回的節點
        int expectedModCount;  // for fast-fail,期望修改次數
        int index;             // current slot,當前的槽

        HashIterator() {
            expectedModCount = modCount;
            Node<K,V>[] t = table;
            current = next = null;
            index = 0;
            if (t != null && size > 0) { // advance to first entry
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }
        public final boolean hasNext() {//是否有下一個
            return next != null;//下一個不為空
        }
        final Node<K,V> nextNode() {//下一個節點
            Node<K,V>[] t;//
            Node<K,V> e = next;//下一個節點
            if (modCount != expectedModCount)//判斷修改次數是否變化過
                throw new ConcurrentModificationException();
            if (e == null)//節點不為空,表示還有后續節點
                throw new NoSuchElementException();
            //如果next是null,就將next賦值為表中下一個不為null的值
            //如果next不是null,那么next就是鏈表或者紅黑樹中的下一個值
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }
        //移除元素
        public final void remove() {
            Node<K,V> p = current;//當前節點
            if (p == null)//如果當前節點為空,拋出異常
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;//將當前節點置為null,避免連續兩次調用移除,并且不移動
            K key = p.key;//key值相等
            //移除node節點
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
}

Android

public Set<Entry<K, V>> entrySet() {
        Set<Entry<K, V>> es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
}
private final class EntrySet extends AbstractSet<Entry<K, V>> {
        public Iterator<Entry<K, V>> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Entry))
                return false;
            Entry<?, ?> e = (Entry<?, ?>) o;
            return containsMapping(e.getKey(), e.getValue());
        }
        public boolean remove(Object o) {
            if (!(o instanceof Entry))
                return false;
            Entry<?, ?> e = (Entry<?, ?>)o;
            return removeMapping(e.getKey(), e.getValue());
        }
        public int size() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        public void clear() {
            HashMap.this.clear();
        }
}
Iterator<Entry<K, V>> newEntryIterator() { return new EntryIterator(); }
private final class EntryIterator extends HashIterator
            implements Iterator<Entry<K, V>> {
        public Entry<K, V> next() { return nextEntry(); }
}
private abstract class HashIterator {
        int nextIndex;
        HashMapEntry<K, V> nextEntry = entryForNullKey;
        HashMapEntry<K, V> lastEntryReturned;
        int expectedModCount = modCount;

        HashIterator() {
            if (nextEntry == null) {
                HashMapEntry<K, V>[] tab = table;
                HashMapEntry<K, V> next = null;
                while (next == null && nextIndex < tab.length) {
                    next = tab[nextIndex++];
                }
                nextEntry = next;
            }
        }

        public boolean hasNext() {
            return nextEntry != null;
        }

        HashMapEntry<K, V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (nextEntry == null)
                throw new NoSuchElementException();

            HashMapEntry<K, V> entryToReturn = nextEntry;
            HashMapEntry<K, V>[] tab = table;
            HashMapEntry<K, V> next = entryToReturn.next;
            while (next == null && nextIndex < tab.length) {
                next = tab[nextIndex++];
            }
            nextEntry = next;
            return lastEntryReturned = entryToReturn;
        }

        public void remove() {
            if (lastEntryReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            HashMap.this.remove(lastEntryReturned.key);
            lastEntryReturned = null;
            expectedModCount = modCount;
        }
}

8.2 Key迭代器

Java

public Set<K> keySet() {
        Set<K> ks;
        return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
//與EntrySet相同,只不過處理的Entry
final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
}
final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().key; }
}

Android

@Override
public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null) ? ks : (keySet = new KeySet());
}
private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            int oldSize = size;
            HashMap.this.remove(o);
            return size != oldSize;
        }
        public void clear() {
            HashMap.this.clear();
        }
}
Iterator<K> newKeyIterator() { return new KeyIterator();   }

private final class KeyIterator extends HashIterator
            implements Iterator<K> {
        public K next() { return nextEntry().key; }
}

8.3 值迭代器

Java

public Collection<V> values() {
        Collection<V> vs;
        return (vs = values) == null ? (values = new Values()) : vs;
}
final class Values extends AbstractCollection<V> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<V> iterator()     { return new ValueIterator(); }
        public final boolean contains(Object o) { return containsValue(o); }
        public final Spliterator<V> spliterator() {
            return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super V> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (Node<K,V> e = tab[i]; e != null; e = e.next)
                        action.accept(e.value);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
}
final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
}

Android

@Override public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null) ? vs : (values = new Values());
}
private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return newValueIterator();
        }
        public int size() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            HashMap.this.clear();
        }
}
Iterator<V> newValueIterator() { return new ValueIterator(); }
private final class ValueIterator extends HashIterator
            implements Iterator<V> {
        public V next() { return nextEntry().value; }
}

8.4 小結

  1. 三個迭代器都是一個迭代器的子類
  2. 如果遍歷Map的話,建議使用EntrySet,這個效率稍高,不用再次調用get方法獲取Value值
  3. Java和Android大同小異,除了Java多了一種數據結構紅黑樹

九、總結

  1. 關于底層數據結構:哈希桶(每個桶里放置鏈表或紅黑樹)
  2. 關于容量和負載因子:容量是哈希表的最大容量,負載因子是哈希Map的存儲比例
    閾值是最大容量*負載因子。而判斷是否超過閾是采用size進行判斷,并不是hashTable中數組的占有比例
  3. 影響哈希Map的效率最主要的方法是key對應類實現的hashCode。
  4. 可以設置key值為null和value值為null
  5. 與HashTable的區別
    4.1. HashMap是線程非安全的,HashTable是線程安全的,Hashtable不允許key和value值為null,
    4.2. 關于Hash值,HashMap中使用了一個函數來處理key.hashCode()方法,Hashtable直接使用了key.hashCode()方法作為hash值,這樣也是為什么key值不能為null,表的位置信息是采用模運算,因為表的長度不是2的n次方。
    4.3. Hashtable擴容是2倍+1,表的長度最小可以設為1。如果使用默認構造函數,那么長度是11,可以理解為默認長度是11.

參考

  1. 散列表(哈希表)的定義
  2. 紅黑樹
  3. Java HashMap工作原理及實現

其他文章

容器解析
ArrayList解析
LinkedList解析
TreeMap解析(上)
TreeMap解析(下)
HashMap解析
LinkedHasMap解析(下)
Set解析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • HashMap 是 Java 面試必考的知識點,面試官從這個小知識點就可以了解我們對 Java 基礎的掌握程度。網...
    野狗子嗷嗷嗷閱讀 6,706評論 9 107
  • 前言 這次我和大家一起學習HashMap,HashMap我們在工作中經常會使用,而且面試中也很頻繁會問到,因為它里...
    liangzzz閱讀 8,025評論 7 102
  • 這則材料關于博物館的禮節,看起來好像有點高大上,當然也是本周最難的聽寫材料了,我發現大家普遍做的都還可以哦~所以大...
    東方閑鶴閱讀 419評論 0 1
  • 轉眼之間,漫畫更新到100啦! 兩個多月的時間里,果子小寶帶給我們家很多歡樂,也帶給我的漫畫很多靈感,感謝幫我帶果...
    tribbie閱讀 257評論 8 9
  • 2016.10.28。 晴。 -5~5度 今天溫度預報得低,但實際上中午出去散步,還是蠻溫暖的。聽...
    amylismile閱讀 164評論 2 0