C# 哈希表加倍?

C# 哈希表加倍?,c#,.net,list,hashtable,C#,.net,List,Hashtable,我不知道标题是否有意义,但我想知道当您向哈希表添加项目时,哈希表是如何放大的 它是否像列表一样,当达到限制时,它的大小会翻倍?如果是这样,那么这个加倍是否会从头开始重新创建集合(这也可以用List来回答,因为我不确定它是否是这样做的) 最后,如果它确实从头开始重新创建,那么对于不知道已达到限制的用户来说,这种特定的添加操作将非常昂贵,对吗?大小并不总是翻倍,而是根据项目的数量有不同的增长 对于一个列表来说,这几乎没有重新创建一个字符串或数组那么昂贵,因为只需要将指针从一个列表复制到另一个列表,这

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

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


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

大小并不总是翻倍,而是根据项目的数量有不同的增长

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


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

我认为
哈希表
字典
在当前计数翻倍后都会扩展到下一个素数,例如31到67

据我所知,调整大小并不涉及重新计算散列(因为它们与条目一起存储),而是涉及将每个条目放入其新的bucket中,其中bucket编号基于散列代码和bucket计数

你问过关于
列表的问题
——这很简单。列表由一个数组支持,您只需创建一个大小合适的新数组,并复制当前数组的内容。比如:

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

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

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

对于.NET哈希表,可以在中指定“加载因子”。从MSDN:

负载系数是最大比率 将元素添加到桶中。较小的负载 因子意味着以成本为代价加快查找速度 内存消耗的增加。A. 负载系数1.0是最佳平衡 在速度和大小之间

从上的MSDN页面:

如果计数小于 在哈希表中,此方法是O(1) 活动如果需要增加容量 增加以适应新的需求 元素,此方法将成为O(n) 操作,其中n是计数

由于列表中有相同的注释,我假设它们在内存分配方面的内部工作类似。

如果有兴趣,为什么不深入研究一下呢:

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();
    }
}
private void Insert(对象键、对象nvalue、bool add)
{
单位数;
单位num2;
if(key==null)
{
抛出新的ArgumentNullException(“key”,Environment.GetResourceString(“ArgumentNull_key”);
}
如果(this.count>=this.loadsize)
{
这个。展开();
}
否则如果((this.occulation>this.loadsize)和&(this.count>100))
{
这个;
}
uint num3=this.InitHash(key,this.bucket.Length,out num,out num2);
int num4=0;
int指数=-1;
int num6=(int)(num%this.bucket.Length);
标签_0071:
if((index=-1)&&(this.buckets[num6].key==this.buckets))&&(this.buckets[num6].hash_coll<0))
{
指数=num6;
}
如果((this.buckets[num6].key==null)| |((this.buckets[num6].key==this.buckets)&((this.buckets[num6].hash_coll&0x8000000L)==0L)))
{
如果(索引!=-1)
{
num6=指数;
}
Thread.BeginCriticalRegion();
this.iswriteringprogress=true;
this.bucket[num6].val=nvalue;
此.bucket[num6].key=key;
this.bucket[num6].hash_coll |=(int)num3;
这个.count++;
这个.UpdateVersion();
this.iswriteringprogress=false;
Thread.EndCriticalRegion();
}
else if(((this.bucket[num6].hash_coll&0x7fffffff)==num3)和&this.KeyEquals(this.bucket[num6].key,key))
{
如果(添加)
{
抛出新的ArgumentException(Environment.GetResourceString(“Argument_AddingDuplicate_uuuz”,新对象[]{this.bucket[num6].key,key}));
}
Thread.BeginCriticalRegion();
this.iswriteringprogress=true;
this.bucket[num6].val=nvalue;
这个.UpdateVersion();
this.iswriteringprogress=false;
Thread.EndCriticalRegion();
}
其他的
{
if((index=-1)&&(this.bucket[num6].hash\u coll>=0))
{
this.bucket[num6].hash_coll |=-2147483648;
这个.占用率++;
}
num6=(int)((num6+num2)%((ulong)this.buckets.Length));
如果(++num4
当然,这一切都取决于您的哈希实现

有些散列加倍,有些将其大小更改为其他任意大小(例如,下一个素数)

大多数哈希在更改缓冲区大小后都需要重新刷新