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
始终为零。
其次,当您通过索引访问列表的元素时,列表不会神奇地增长,只有当您调用 Add
或 Insert
(或AddRange
或 InsertRange
,你明白了要点)。即使它们确实增长了,您仍然不能访问 Count
和 Capacity
之间的任何索引。
我怀疑第二种方法 "never caused an exception",因为它是同一个问题:您正在创建一个具有特定容量的空列表,然后立即尝试访问超出其计数的索引。 C# 不像 C++,在 C++ 中,这可以归结为未定义的行为:在这里,它会抛出,每次。
由于您的算法似乎是 "create a list of a predetermined length then replace individual elements",数组可能更合适。
所以,我使用下面的代码有一段时间了,直到最近它才抛出异常。
// 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
始终为零。
其次,当您通过索引访问列表的元素时,列表不会神奇地增长,只有当您调用 Add
或 Insert
(或AddRange
或 InsertRange
,你明白了要点)。即使它们确实增长了,您仍然不能访问 Count
和 Capacity
之间的任何索引。
我怀疑第二种方法 "never caused an exception",因为它是同一个问题:您正在创建一个具有特定容量的空列表,然后立即尝试访问超出其计数的索引。 C# 不像 C++,在 C++ 中,这可以归结为未定义的行为:在这里,它会抛出,每次。
由于您的算法似乎是 "create a list of a predetermined length then replace individual elements",数组可能更合适。