我正在遍历一个潜在的巨大(数百万个项目)数据集(存储在磁盘上),并提取要添加到List<T>的选定项目。当我将一个项目添加到列表时,我会在其周围放一个锁,因为还有其他线程正在访问该列表。

我试图在两种可能的实现方式之间做出决定:

1)每次我需要添加项目时锁定列表。

2)使用找到要添加项目的临时列表,然后使用List<T>.AddRange()以大块方式添加该列表中的项目(例如,当我找到1000个匹配项时)。这导致需要较少地请求对列表进行锁定,但是如果AddRange()仅增加足够的容量以恰好容纳新项目,则列表将最终被重新调整大小。

我的问题是:据我了解,每次添加一个项目将导致List<T>的内部容量每次达到容量时的两倍,但是我不知道List<T>.AddRange()的行为。我认为它只会增加足够的容量来容纳新物品,但是我找不到任何方法可以确认这一点。对于Add()和AddRange(),关于MSDN上如何增加容量的描述几乎相同,除了对于AddRange而言,它表示如果新计数大于容量,则增加容量,而不是如果Count已经是和容量一样
对我来说,这似乎是使用AddRange()添加足够的项目以超过当前容量将导致容量增加,就像使用Add()超过当前容量一样。

那么,使用List<T>.AddRange()在大到足以超过当前容量的块中添加项目会导致容量仅增加到足以容纳新项目的能力,还是会导致容量增加一倍?还是它做了我什至没有考虑的其他事情?

希望没有任何代码示例就足够清楚了,因为这是有关如何实现List<T>的一般问题,但是如果没有,我将添加任何使我的问题更清楚的代码。
如前所述,我已经阅读了MSDN文档,找不到明确的答案。我也在这里搜索了任何类似的问题,但没有找到任何问题,但是如果有我错过的问题,请指向我!

最佳答案

只要传递为AddRange参数的集合实现了ICollection<T>,数组大小就只会增加一次:

ICollection<T> collection2 = collection as ICollection<T>;
if (collection2 != null)
{
    int count = collection2.Count;
    if (count > 0)
    {
        this.EnsureCapacity(this._size + count);

    // (...)


否则,对每个元素进行标准枚举和Insert方法调用:

}
else
{
    using (IEnumerator<T> enumerator = collection.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            this.Insert(index++, enumerator.Current);
        }
    }
}


编辑

查看EnsureCapacity方法:

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


它增加了Max(old_size * 2, min)的数组大小,并且因为它是用min = old_size + count调用的,因此AddRange调用后的最终数组大小将设置为Max(old_size * 2, old_size + count)-它将警告当前的List<T>大小和集合的大小使用AddRange方法添加。

关于c# - 使用AddRange()时如何增加List <T>的内部数组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18573115/

10-13 08:57