Как инициализировать List <T> для заданного размера (в отличие от емкости)?

.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"

РЕДАКТИРОВАТЬ 2: Люди задаются вопросом, почему я хочу использовать списки таким образом, поскольку это не так, как они предназначены для использования. Я вижу две причины:

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

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

Ответ 1

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

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(), но это будет неэффективно из-за изменения размера буфера.

Обратите внимание, что если T является ссылочным типом, он будет хранить count копии ссылки, переданной для параметра value, поэтому все они будут ссылаться на один и тот же объект. Это может или не может быть тем, что вы хотите, в зависимости от вашего варианта использования.

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

Ответ 2

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

Ответ 3

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

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

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

EDIT2:

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

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

Ответ 4

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

Когда вы это сделаете:

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

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

Ответ 5

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

Ответ 6

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

int[] fakeArray = new int[10];

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

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

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

Ответ 10

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

Ответ 11

Принятый ответ (с зеленой галочкой) имеет проблему.

Эта проблема:

var result = Lists.Repeated(new MyType(), sizeOfList);
// each item in the list references the same MyType() object
// if you edit item 1 in the list, you are also editing item 2 in the list

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

Если вы хотите инициализировать каждый элемент в вашем списке конструктором по умолчанию, а не NULL, добавьте следующий метод:

public static List<T> RepeatedDefaultInstance<T>(int count)
    {
        List<T> ret = new List<T>(count);
        for (var i = 0; i < count; i++)
        {
            ret.Add((T)Activator.CreateInstance(typeof(T)));
        }
        return ret;
    }

Ответ 12

Вы можете использовать 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();

Ответ 13

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

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

Ответ 14

Это образец, который я использовал для моего unit test. Я создал список объектов класса. Затем я использовал 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);


    }

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

Ответ 15

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

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

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

Однако распределение списков циклов может быть быстрее, поскольку List является встроенным классом, но С# является jit-compiled sooo...