Как инициализировать список <T>для заданного размера (в отличие от емкости)? - PullRequest
90 голосов
/ 21 января 2009

.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);

Что было бы чище?

РЕДАКТИРОВАТЬ: ответы до сих пор относятся к емкости, которая является чем-то иным, чем предварительное заполнение списка. Например, в только что созданном списке с емкостью 10 нельзя сделать L[2]="somevalue"

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

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

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

Ответы [ 13 ]

106 голосов
/ 10 марта 2009
List<string> L = new List<string> ( new string[10] );
68 голосов
/ 22 января 2009

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

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

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

23 голосов
/ 21 января 2009

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

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

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

EDIT2:

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

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

7 голосов
/ 21 января 2009

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

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

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

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

5 голосов
/ 02 сентября 2016

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

int[] fakeArray = new int[10];

List<int> list = fakeArray.ToList();
5 голосов
/ 21 января 2009

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

4 голосов
/ 23 января 2009

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

Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";
4 голосов
/ 22 января 2009

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

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}
1 голос
/ 22 января 2009
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();
0 голосов
/ 30 ноября 2016

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

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

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

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

...