Hashtable加倍?

时间:2009-04-16 19:11:25

标签: c# .net list hashtable

我不知道标题是否有意义,但我想知道当你向它添加项目时哈希表是如何放大的?

它是否像List<T>那样,当达到限制时,它的大小加倍?如果是这样,那么这个加倍会从头开始重新创建集合(这也可以回答List<T>,因为我不确定它是不是这样做了吗?

最后如果它确实从头开始重新创建它,那么对于不知道达到限制的用户来说,这个特殊的Add操作会非常昂贵,对吗?

6 个答案:

答案 0 :(得分:5)

我认为HashtableDictionary<TKey, TValue>在将当前计数加倍后会扩展到下一个素数,例如31至67岁。

据我了解,调整大小不会涉及重新计算哈希值(因为它们与条目一起存储),但涉及将每个条目放入其新桶中,其中桶号基于在哈希码和桶数上。

你问过List<T> - 这真的很简单。该列表由数组支持,您只需创建一个具有正确大小的新数组,并复制当前数组的内容。类似的东西:

private void Resize(int newCapacity)
{
    T[] tmp = new T[newCapacity];
    Array.Copy(backingArray, tmp, backingArray.Length);
    backingArray = tmp;
}

答案 1 :(得分:1)

哈希表通过使用桶来工作,每个桶可以容纳多个项目(至少在大多数实现中,有一些在已经使用过的桶的情况下重用其他桶)。桶的数量通常是素数,因此将哈希码除以桶的数量会返回“好”哈希值的可接受分布。

通常,有一个填充因子会触发添加更多桶,从而重建哈希表。由于哈希值除以桶数,因此需要根据新的桶索引重新分配实例,这基本上是从头开始重新创建。

对于.NET哈希表,您可以在some constructors中指定“加载因子”。来自MSDN:

  

负载系数是最大比率   元素到桶。负载较小   因素意味着更快的查找成本   增加内存消耗。一个   载荷系数1.0是最佳平衡   在速度和大小之间。

答案 2 :(得分:0)

尺寸并不总是加倍,但根据项目数量有不同的增长。

对于列表,这并不像重新创建字符串或数组那么昂贵,因为只需要将指针从一个列表复制到另一个列表,这可以非常有效地完成。

对于散列表/字典,必须重新分配项目,这可能非常昂贵。最好提前估计大小的哈希表。

答案 3 :(得分:0)

来自Hashtable.Add()上的MSDN页面:

  

如果Count小于容量   Hashtable,这个方法是O(1)   操作。如果容量需要   增加以适应新的   元素,此方法成为O(n)   操作,其中n是计数。

由于List具有相同的注释,我认为它们内部的内存分配方式类似。

答案 4 :(得分:0)

如果感兴趣,为什么不深入reflector做一些研究呢?

private void Insert(object key, object nvalue, bool add)
{
    uint num;
    uint num2;
    if (key == null)
    {
        throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
    }
    if (this.count >= this.loadsize)
    {
        this.expand();
    }
    else if ((this.occupancy > this.loadsize) && (this.count > 100))
    {
        this.rehash();
    }
    uint num3 = this.InitHash(key, this.buckets.Length, out num, out num2);
    int num4 = 0;
    int index = -1;
    int num6 = (int) (num % this.buckets.Length);
Label_0071:
    if (((index == -1) && (this.buckets[num6].key == this.buckets)) && (this.buckets[num6].hash_coll < 0))
    {
        index = num6;
    }
    if ((this.buckets[num6].key == null) || ((this.buckets[num6].key == this.buckets) && ((this.buckets[num6].hash_coll & 0x80000000L) == 0L)))
    {
        if (index != -1)
        {
            num6 = index;
        }
        Thread.BeginCriticalRegion();
        this.isWriterInProgress = true;
        this.buckets[num6].val = nvalue;
        this.buckets[num6].key = key;
        this.buckets[num6].hash_coll |= (int) num3;
        this.count++;
        this.UpdateVersion();
        this.isWriterInProgress = false;
        Thread.EndCriticalRegion();
    }
    else if (((this.buckets[num6].hash_coll & 0x7fffffff) == num3) && this.KeyEquals(this.buckets[num6].key, key))
    {
        if (add)
        {
            throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate__", new object[] { this.buckets[num6].key, key }));
        }
        Thread.BeginCriticalRegion();
        this.isWriterInProgress = true;
        this.buckets[num6].val = nvalue;
        this.UpdateVersion();
        this.isWriterInProgress = false;
        Thread.EndCriticalRegion();
    }
    else
    {
        if ((index == -1) && (this.buckets[num6].hash_coll >= 0))
        {
            this.buckets[num6].hash_coll |= -2147483648;
            this.occupancy++;
        }
        num6 = (int) ((num6 + num2) % ((ulong) this.buckets.Length));
        if (++num4 < this.buckets.Length)
        {
            goto Label_0071;
        }
        if (index == -1)
        {
            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HashInsertFailed"));
        }
        Thread.BeginCriticalRegion();
        this.isWriterInProgress = true;
        this.buckets[index].val = nvalue;
        this.buckets[index].key = key;
        this.buckets[index].hash_coll |= (int) num3;
        this.count++;
        this.UpdateVersion();
        this.isWriterInProgress = false;
        Thread.EndCriticalRegion();
    }
}

答案 5 :(得分:0)

当然,这完全取决于你的哈希实现。

某些哈希值加倍,有些哈希值会将其大小更改为其他任意大小(例如,下一个素数)。

大多数哈希值在更改缓冲区大小后需要重新散列,这只是“只是”移动指针,但仍然与散列大小成线性关系。但是,一些散列使用一致的散列,这减少了移动元素的需要(通常,只需要移动一小部分元素)。