Как инициализировать список до заданного размера (в отличие от емкости)?


.NET предлагает общий контейнер списков, производительность которого почти идентична (см. раздел производительность массивов и списков). Однако они совершенно разные по инициализации.

массивы очень легко инициализировать со значением по умолчанию, и по определению они уже имеют определенный размер:

string[] Ar = new string[10];

что позволяет безопасно назначать случайные предметы, скажем:

Ar[5]="hello";

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

List<string> L = new List<string>(10);
for (int i=0;i<10;i++) L.Add(null);

или

string[] Ar = new string[10];
List<string> L = new List<string>(Ar);

что было бы более чистым способом?

EDIT: ответы до сих пор относятся к емкости, что является чем-то иным, чем предварительное заполнение списка. Например, в списке, только что созданном с емкостью 10, нельзя сделать L[2]="somevalue"

EDIT 2: люди задаются вопросом, почему я хочу использовать списки таким образом, так как это не так, как они предназначены для использования. Я вижу два причины:

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

  2. то, что я сейчас пишу, - это базовый класс, предлагающий функциональность по умолчанию как часть более крупной структуры. В функциональности по умолчанию, которую я предлагаю, размер списка известен в advanced и поэтому я мог бы использовать массив. Однако я хочу предложить любому базовому классу возможность динамически расширять его, и поэтому я выбираю список.

14 79

14 ответов:

Я не могу сказать, что очень часто - не могли бы вы дать более подробную информацию, почему вы хотите этого? Я бы, вероятно, поставил его как статический метод в вспомогательном классе:

public static class Lists
{
    public static List<T> RepeatedDefault<T>(int count)
    {
        return Repeated(default(T), count);
    }

    public static List<T> Repeated<T>(T value, int count)
    {
        List<T> ret = new List<T>(count);
        ret.AddRange(Enumerable.Repeat(value, count));
        return ret;
    }
}

вы может использовать Enumerable.Repeat(default(T), count).ToList() но это было бы неэффективно из-за размера буфера.

EDIT: как отмечено в комментариях, вы можете сделать Repeated используйте цикл для заполнения списка, если вы хотите. Это тоже было бы немного быстрее. Лично я нахожу код с помощью Repeat более описательным, и подозреваю, что в реальном мире разница в производительности будет несущественной, но ваш пробег может отличаться.

List<string> L = new List<string> ( new string[10] );

используйте конструктор, который принимает int ("емкость") в качестве аргумента:

List<string> = new List<string>(10);

EDIT: я должен добавить, что я согласен с Фредериком. Вы используете список таким образом, что идет вразрез со всеми рассуждениями об использовании его в первую очередь.

EDIT2:

EDIT 2: то, что я сейчас пишу, является базовым классом, предлагающим функциональность по умолчанию как часть большей структуры. В функциональности по умолчанию, которую я предлагаю, размер списка известен в передовые и поэтому я мог бы использовать массив. Однако я хочу предложить любому базовому классу возможность динамически расширять его, и поэтому я выбираю список.

зачем кому-то нужно знать размер список со всеми значениями null? Если в списке нет реальных значений, я ожидаю, что длина будет равна 0. Во всяком случае, тот факт, что это неуклюжий демонстрирует, что он идет против предполагаемого использования класса.

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

при этом:

List<int> = new List<int>(100);

создать список емкость которого составляет 100 целых чисел. Это означает, что ваш список не будет расти, пока вы не добавите элемент 101-й. Базовый массив списка будет инициализирован с длиной 100.

инициализация содержимого такого списка-это не совсем то, для чего нужны списки. Списки предназначены для хранения объектов. Если вы хотите сопоставить определенные числа с определенными объектами, рассмотрите возможность использования структуры пары ключ-значение, такой как хэш-таблица или словарь, а не список.

Если вы хотите инициализировать список с N элементами некоторого фиксированного значения:

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}

вы, кажется, подчеркиваете необходимость позиционной ассоциации с вашими данными, так что ассоциативный массив не будет более подходящим?

Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";

сначала создайте массив с нужным количеством элементов, а затем преобразуйте массив в список.

int[] fakeArray = new int[10];

List<int> list = fakeArray.ToList();

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

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

List<string> = new List<string>(10);
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();

Вы можете использовать Linq для умной инициализации списка со значением по умолчанию. (Похоже на ответ Дэвида Б.)

var defaultStrings = (new int[10]).Select(x => "my value").ToList();

пройдите еще один шаг и инициализируйте каждую строку с различными значениями "строка 1", "строка 2", "строка 3" и т. д.:

int x = 1;
var numberedStrings = (new int[10]).Select(x => "string " + x++).ToList();

уведомление об IList: замечания MSDN IList: "Реализации IList делятся на три категории: только для чтения, фиксированный размер и переменный размер. (...). Общую версию этого интерфейса см. В разделе System.Collections.Generic.IList<T>."

IList<T> не наследует от IListList<T> реализует как IList<T> и IList), но всегда переменной размере. Начиная с .NET 4.5, мы также IReadOnlyList<T> но AFAIK, нет фиксированного размера общего списка, который был бы тем, что вы ищем.

Это пример, который я использовал для моего модульного теста. Я создал список объектов класса. Затем я использовал forloop, чтобы добавить " X " количество объектов, которые я ожидаю от службы. Таким образом, Вы можете добавить/инициализировать Список для любого заданного размера.

public void TestMethod1()
    {
        var expected = new List<DotaViewer.Interface.DotaHero>();
        for (int i = 0; i < 22; i++)//You add empty initialization here
        {
            var temp = new DotaViewer.Interface.DotaHero();
            expected.Add(temp);
        }
        var nw = new DotaHeroCsvService();
        var items = nw.GetHero();

        CollectionAssert.AreEqual(expected,items);


    }

надеюсь, я вам помог, ребята.

немного поздно, но первое решение, которое вы предложили, кажется мне намного чище : вы не выделяете память дважды. Даже list constrcutor должен перебирать массив, чтобы скопировать его; он даже не знает заранее, что внутри есть только нулевые элементы.

1. - выделить N - петля N Стоимость: 1 * выделить(N) + N * loop_iteration

2. - выделить N - выделить N + петли () Стоимость: 2 * выделить(N) + N * loop_iteration

однако распределение списка петель может быть быстрее, так как List является встроенным классом, но C# является JIT-скомпилированным sooo...