Как инициализировать список до заданного размера (в отличие от емкости)?
.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: люди задаются вопросом, почему я хочу использовать списки таким образом, так как это не так, как они предназначены для использования. Я вижу два причины:
можно вполне убедительно утверждать, что списки являются массивами "следующего поколения", добавляя гибкость почти без штрафов. Поэтому их следует использовать по умолчанию. Я указываю, что они могут быть не так легко инициализировать.
то, что я сейчас пишу, - это базовый класс, предлагающий функциональность по умолчанию как часть более крупной структуры. В функциональности по умолчанию, которую я предлагаю, размер списка известен в advanced и поэтому я мог бы использовать массив. Однако я хочу предложить любому базовому классу возможность динамически расширять его, и поэтому я выбираю список.
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
более описательным, и подозреваю, что в реальном мире разница в производительности будет несущественной, но ваш пробег может отличаться.
используйте конструктор, который принимает 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);
Вы можете использовать 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>
не наследует отIList
(аList<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...