美文网首页
JAVA非并发容器--HashMap-扩容策略

JAVA非并发容器--HashMap-扩容策略

作者: 小犇手K线研究员 | 来源:发表于2017-03-14 21:17 被阅读93次

    概述

    我相信只要写过JAVA的程序要拿99%的都用过HashMap, 其是我们最常用,也是最基础的一个Map.本篇文章将从存储结构、hash规则、扩容策略、迭代器四方面来分析其源代码。

    扩容策略

    从HashMap的存储结构来讲,由于有链表,所以可以存储无限个元素,但是链表越长
    hash冲突就越多,查询效率越低,所以需要对数组进行扩容和rehash.
    put(key, value)方法如下:

    public V put(K key, V value) {
            if (table == EMPTY_TABLE) {
                inflateTable(threshold);
            }
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key);
            int i = indexFor(hash, table.length);
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
    
            modCount++;
            addEntry(hash, key, value, i);
            return null;
        }
    

    当table是空数组的时候,需要首先调用inflateTable(threshold)初始话素组的大小,其代码如下:

    private void inflateTable(int toSize) {
            // Find a power of 2 >= toSize
            int capacity = roundUpToPowerOf2(toSize);
    
            threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
            table = new Entry[capacity];
            initHashSeedAsNeeded(capacity);
        }
    

    当不为空的时候,调用 addEntry(hash, key, value, i):

      void addEntry(int hash, K key, V value, int bucketIndex) {
            if ((size >= threshold) && (null != table[bucketIndex])) {
                resize(2 * table.length);
                hash = (null != key) ? hash(key) : 0;
                bucketIndex = indexFor(hash, table.length);
            }
    
            createEntry(hash, key, value, bucketIndex);
        }
    

    条件(size >= threshold) && (null != table[bucketIndex])和2*table.length就是扩容规则:
    1:当元素个数大于阈值(一般是table.lengthx0.75),并且table[index]不为空时,扩容,为什么要不为空时?为空的时候说明table的槽还没有用完,hash冲突严重。
    2:将数组扩大到原来的2倍.
    resize()函数:

    void resize(int newCapacity) {
            Entry[] oldTable = table;
            int oldCapacity = oldTable.length;
            if (oldCapacity == MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return;
            }
    
            Entry[] newTable = new Entry[newCapacity];
            transfer(newTable, initHashSeedAsNeeded(newCapacity));
            table = newTable;
            threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
        }
    

    transfer()如下:

    void transfer(Entry[] newTable, boolean rehash) {
            int newCapacity = newTable.length;
            for (Entry<K,V> e : table) {
                while(null != e) {
                    Entry<K,V> next = e.next;
                    if (rehash) {
                        e.hash = null == e.key ? 0 : hash(e.key);
                    }
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:JAVA非并发容器--HashMap-扩容策略

          本文链接:https://www.haomeiwen.com/subject/jgcknttx.html