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

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何理解 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 原本的特性。

以上是关于如何理解 List<T;和 Dictionary<K,V; 的扩容机制 ?的主要内容,如果未能解决你的问题,请参考以下文章

java中private list<customer> customers;的<customer>是啥意思如何理解

浅显理解Java泛型的super和extends

IEnumeration<T> 和 List<T> 之间的区别? [复制]

List<T> 的 C# ASP.NET MVC 模型绑定

Java 泛型 lt;super T>中 super 怎么 理解?与 extends 有何不同

如何连接 IQueryable<T> 和 List<T>