Есть ли в С# метод для List <T>, как изменение размера в С++ для вектора <T>
Когда я использую resize(int newsize)
в С++ для vector<T>
, это означает, что size
этого vector
установлен в newsize
, а индексы работают в диапазоне [0..newsize)
. Как сделать то же самое в С# для List<T>
?
Изменение свойства List<T>
Capacity
изменяет только Capacity
, но оставляет Count
тем же, и, кроме того, индексы все еще находятся в диапазоне [0..Count)
. Помогите мне, пожалуйста.
P.S. Представьте, что у меня есть vector<T> tmp
с tmp.size() == 5
, я не могу ссылаться на tmp[9]
, но когда я использую tmp.resize(10)
, я могу обратиться к tmp[9]
. В С#, если у меня есть List<T> tmp
с tmp.Count == 5
, я не могу ссылаться на tmp[9]
(IndexOutOfRangeException
), но даже когда я устанавливаю tmp.Capacity=10
, я не буду ссылаться на tmp[9]
coz tmp.Count
еще 5. Я хочу найти некоторую аналогию с изменением размера в С#.
Ответы
Ответ 1
Нет, но вы можете использовать методы расширения, чтобы добавить свои собственные. Следующее имеет такое же поведение, как std::vector<T>::resize()
, включая ту же сложность времени. Единственное отличие состоит в том, что в С++ мы можем определить значение по умолчанию с void resize ( size_type sz, T c = T() )
, а способ работы с шаблонами означает, что это прекрасно, если мы будем называть его без значения по умолчанию для T
, у которого нет доступного конструктора без параметров. В С# мы не можем этого сделать, поэтому вместо этого мы должны создать один метод без ограничения, который соответствует случаю, не используемому по умолчанию, а другой с ограничением where new()
, который вызывает его.
public static class ListExtra
{
public static void Resize<T>(this List<T> list, int sz, T c)
{
int cur = list.Count;
if(sz < cur)
list.RemoveRange(sz, cur - sz);
else if(sz > cur)
{
if(sz > list.Capacity)//this bit is purely an optimisation, to avoid multiple automatic capacity changes.
list.Capacity = sz;
list.AddRange(Enumerable.Repeat(c, sz - cur));
}
}
public static void Resize<T>(this List<T> list, int sz) where T : new()
{
Resize(list, sz, new T());
}
}
Теперь подобные myList.Resize(23)
или myList.Resize(23, myDefaultValue)
будут соответствовать ожиданиям от вектора С++. Я бы заметил, что иногда, когда с С++ у вас будет вектор указателей, в С# у вас будет список некоторых ссылочных типов. Следовательно, в случаях, когда С++ T()
создает нулевой указатель (потому что это указатель), здесь мы ожидаем, что он вызовет конструктор без параметров. По этой причине вы можете найти его ближе к поведению, которое вы использовали для замены второго метода:
public static void Resize<T>(this List<T> list, int sz)
{
Resize(list, sz, default(T));
}
Это оказывает такое же влияние на типы значений (calllessless constructor), но с ссылочными типами он заполняет нулями. В этом случае мы можем просто переписать весь класс следующим образом:
public static class ListExtra
{
public static void Resize<T>(this List<T> list, int sz, T c = default(T))
{
int cur = list.Count;
if(sz < cur)
list.RemoveRange(sz, cur - sz);
else if(sz > cur)
list.AddRange(Enumerable.Repeat(c, sz - cur));
}
}
Обратите внимание, что речь идет не о различиях между std::vector<T>
и List<T>
, а о различиях в том, как указатели используются в С++ и С#.
Ответ 2
Просто чтобы сделать Jon Hanna answer более читаемым:
public static class ListExtras
{
// list: List<T> to resize
// size: desired new size
// element: default value to insert
public static void Resize<T>(this List<T> list, int size, T element = default(T))
{
int count = list.Count;
if (size < count)
{
list.RemoveRange(size, count - size);
}
else if (size > count)
{
if (size > list.Capacity) // Optimization
list.Capacity = size;
list.AddRange(Enumerable.Repeat(element, size - count));
}
}
}
Ответ 3
извините. это то, что вам нужно?
List.TrimExcess()
Ответ 4
Настройка List<T>.Capacity
похожа на использование std::vector<T>.reserve(..)
. Возможно, List<T>.AddRange(..)
соответствует вашим потребностям.
Ответ 5
Разве вы не читали в MSDN: -
Список представляет собой изменяемую по размеру коллекцию элементов. Списки могут быть построены несколько способов, но наиболее полезным классом является List. Это позволяет вам чтобы строго указать ваш список, включает в себя все необходимые функциональность для работы с коллекциями и может быть легко поиск.
Далее: -
Емкость - это количество элементов, которые список может хранить до требуется изменить размер, а Count - количество элементов, которые фактически в списке.
Емкость всегда больше или равна Count. Если граф превышает Производительность при добавлении элементов увеличивает емкость на автоматически перераспределяя внутренний массив перед копированием старого элементы и добавление новых элементов.
Ответ 6
Это мое решение.
private void listResize<T>(List<T> list, int size)
{
if (size > list.Count)
while (size - list.Count > 0)
list.Add(default<T>);
else if (size < list.Count)
while (list.Count - size > 0)
list.RemoveAt(list.Count-1);
}
Когда значения size
и list.Count
совпадают, нет необходимости изменять размер списка.
Параметр default(T)
используется вместо null
, ""
, 0
или других типов с нулевым значением для заполнения пустого элемента в списке, потому что мы не знаем, какой тип <T>
( ссылка, значение, структура и т.д.).
P.S. Я использовал циклы for
вместо циклов while
, и я столкнулся с проблема. Я не всегда имел размер списка, о котором я просил. Это было меньше. Любые мысли, почему?
Проверьте это:
private void listResize<T>(List<T> list, int size)
{
if (size > list.Count)
for (int i = 0; i <= size - list.Count; i++)
list.Add(default(T));
else if (size < list.Count)
for (int i = 0; i <= list.Count - size; i++)
list.RemoveAt(list.Count-1);
}
Ответ 7
Список не имеет конечного размера.
Есть ли причина, по которой размер имеет значение для вас?
Возможно, массив или словарь ближе к вашим требованиям