C #: вызов списка <T>. Элемент [Int32] с ненаселенным списком иногда вызывает исключение, но не всегда - PullRequest
0 голосов
/ 19 декабря 2018

Итак, некоторое время я использовал следующий бит кода, и до недавнего времени он никогда не выдавал исключение.

// 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];

В частности, левая часть, где я пытаюсь установить значение по индексу.Я получаю сообщение об исключении:

"Индекс был вне диапазона. Должен быть неотрицательным и меньше размера коллекции. \ R \ nИмя параметра: индекс"

Читая часть онлайн-документации Microsoft , выясняется, что она действительно должна выбросить ArgumentOutOfRangeException, когда index больше или равно Count.

Однако, как вы можете видеть далее в коде, я использую тот же метод для заполнения списка, и это никогда не вызывало исключение.Я также очень скептически отношусь к тому, что этот конкретный блок кода никогда не вызывался раньше, так как для этого требовалось бы, чтобы все наборы данных, в которых этот код использовался ранее, имели размеры, которые были естественными степенями 5.

Этоэто самый большой набор данных, на котором я использовал этот код (1366921 элементов!), так мог ли это иметь какой-либо эффект?(В идеале это не должно быть, но вы никогда не знаете ...)

Пока я просто переписываю это, используя массивы, но я бы хотел понять, что здесь происходит.Я всегда просто предполагал, что адресация по индексу в списке автоматически расширит список, если он выйдет за пределы текущего размера, и меня беспокоит, как это повлияет на мои старые проекты.

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Во-первых, конструктор List<T>, который принимает параметр int, инициализирует список с указанной емкость , а не длина .Это означает, что результирующий список всегда будет иметь начальное значение Count, равное нулю.

Во-вторых, списки магически не растут, когда вы обращаетесь к их элементам по индексу, только когда вы вызываете Add или Insert (или AddRange или InsertRange, вы получите суть).И даже когда они растут, вам по-прежнему не разрешен доступ к любому из индексов между Count и Capacity.

Я скептически отношусь к тому, что второй метод "никогда не вызывал исключения", посколькуэто та же проблема: вы создаете пустой список с определенной емкостью, а затем сразу же пытаетесь получить доступ к индексам за пределами его подсчета.C # не похож на C ++, где это можно отнести к неопределенному поведению: здесь он будет выдавать каждый раз .

Поскольку ваш алгоритм выглядит так: «создайте списокзаданной длины, а затем заменить отдельные элементы ", массивы, вероятно, лучше подходят.

0 голосов
/ 19 декабря 2018
groupMediansList = new List<KeyValuePair<int, float>>(fullGroupListLength + 1);

Похоже, вы работаете в предположении, что установка емкости списка на fullGroupListLength + 1 позволяет вам получать доступ к индексам от 0 до fullGroupListLength.Однако Capacity и Count - это две разные вещи.Вы не можете получить доступ к индексу, большему или равному groupMediansList.Count.Решением будет добавление необходимого количества значений в список или использование другой коллекции, например массива.

...