C#:使用未填充的列表调用 List<T>.Item[Int32] 有时会引发异常,但并非总是如此

C#: calling List<T>.Item[Int32] with an unpopulated list throws an exception sometimes, but not always

所以,我使用下面的代码有一段时间了,直到最近它才抛出异常。

// median-of-medians search:
const int MOM_GROUP_SIZE = 5;
List<KeyValuePair<int, float>> mediansList = indexPositionPairs;
while (mediansList.Count > 1) // could be only one because of outer loop, so check before first (inner) iteration!
{
    List<KeyValuePair<int, float>> groupMediansList;
    int fullGroupListLength = mediansList.Count / MOM_GROUP_SIZE;
    int remainderGroupSize = mediansList.Count % MOM_GROUP_SIZE;
    if (remainderGroupSize > 0)
    {
        groupMediansList = new List<KeyValuePair<int, float>>(fullGroupListLength + 1);
        // find last group median
        int startingIndex = fullGroupListLength * MOM_GROUP_SIZE;
        mediansList.Sort(startingIndex, remainderGroupSize, comp);
        groupMediansList[fullGroupListLength] = mediansList[startingIndex + remainderGroupSize / 2];
    }
    else
    {
        groupMediansList = new List<KeyValuePair<int, float>>(fullGroupListLength);
    }

    // groups of 5:
    for (int i = 0, j = 0; i < fullGroupListLength; ++i, j += MOM_GROUP_SIZE)
    {
        mediansList.Sort(j, MOM_GROUP_SIZE, comp);
        groupMediansList[i] = mediansList[j + MOM_MEDIAN_OFFSET];
    }

    // repeat on the group medians until only one remains
    mediansList = groupMediansList;
}

现在我从以下行中得到 ArgumentOutOfRangeException

groupMediansList[fullGroupListLength] = mediansList[startingIndex + remainderGroupSize / 2];

具体来说,左侧,我试图在索引处设置一个值。我收到异常消息:

"Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index"

阅读 Microsoft's online documentation 的一些内容,似乎确实应该在 index 大于或等于 Count 时抛出 ArgumentOutOfRangeException

但是,正如您在代码中进一步看到的那样,我使用相同的方法来填充列表,并且从未导致异常。我也非常怀疑这个特定的代码块以前从未被调用过,因为它要求以前使用此代码的所有数据集的大小都是 5 的自然幂。

这是迄今为止我使用此代码处理过的最大数据集(1366921 个元素!),那么这有什么影响吗? (理想情况下它不应该,但你永远不知道......)

现在,我将使用数组重写它,但我想了解这里发生了什么。我一直只是假设如果列表超出当前大小,按索引在列表上寻址会自动扩展列表,我担心这对我的旧项目有什么影响。

groupMediansList = new List<KeyValuePair<int, float>>(fullGroupListLength + 1);

您似乎在假设将列表的容量设置为 fullGroupListLength + 1 允许您访问索引 0 到 fullGroupListLength。但是,Capacity 和 Count 是两个不同的东西。您不能访问大于或等于 groupMediansList.Count 的索引。解决方案是将所需数量的值添加到列表中,或者使用不同的集合,如数组。

首先,采用 int 参数的 List<T> 构造函数使用指定的 容量 初始化列表,而不是 长度 。这意味着结果列表的初始 Count 始终为零。

其次,当您通过索引访问列表的元素时,列表不会神奇地增长,只有当您调用 AddInsert(或AddRangeInsertRange,你明白了要点)。即使它们确实增长了,您仍然不能访问 CountCapacity 之间的任何索引。

我怀疑第二种方法 "never caused an exception",因为它是同一个问题:您正在创建一个具有特定容量的空列表,然后立即尝试访问超出其计数的索引。 C# 不像 C++,在 C++ 中,这可以归结为未定义的行为:在这里,它会抛出,每次

由于您的算法似乎是 "create a list of a predetermined length then replace individual elements",数组可能更合适。