如何理解List<T>和Dictionary<K,V>的扩容机制?

咨询区

  • Royi Namir

为什么 List 是按照 2倍 扩容。.

private void EnsureCapacity(int min)
{
    if (this._items.Length < min)
    {
        int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2);
        if (num < min)
        {
            num = min;
        }
        this.Capacity = num;
    }
}

而 Dictionary<K,V> 是按 素数 扩容。

private void Resize()
{
    int prime = HashHelpers.GetPrime(this.count * 2);
    int[] numArray = new int[prime];
    for (int i = 0; i < numArray.Length; i++)
    {
        numArray[i] = -1;
    }
    Entry<TKey, TValue>[] destinationArray = new Entry<TKey, TValue>[prime];
    Array.Copy(this.entries, 0, destinationArray, 0, this.count);
    for (int j = 0; j < this.count; j++)
    {
        int index = destinationArray[j].hashCode % prime;
        destinationArray[j].next = numArray[index];
        numArray[index] = j;
    }
    this.buckets = numArray;
    this.entries = destinationArray;
}

为什么不都按照 2倍 来呢?这样还可以实现代码复用。

回答区

  • Gman

Dictionary 是启发式的,它需要保证 hashcode 必须准均匀的分布在各个桶中,.NET 的 Dictionary 采用的是素数来实现这个均衡,下面是计算桶索引的算法。

int num = this.comparer.GetHashCode(key) & 2147483647; // make hash code positive
// get the remainder from division - that's our bucket index
int num2 = this.buckets[num % ((int)this.buckets.Length)];

不过 Java 采用的和 .NET 中的 List 是一样的扩容机制。

resize(2 * table.length);

在翻倍之后,java 会重新计算 hashcode 值的。

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
    return h & (length-1);
}

// from put() method
int hash = hash(key.hashCode()); // get modified hash
int i = indexFor(hash, table.length); // trim the hash to the bucket count

List 不是启发式的,所以没有这么烦恼,

点评区

其实说到底,Dictionary采用素数,本质目的就是减少桶挂链,减少hashcode冲突,如果挂链过长,那就达不到 Contains,Add,Get 等操作的 O(1) 时间复杂度,这也就失去了 Dictionary 原本的特性。