Подтвердить что ты не робот

Список <T> Уменьшение размера, если вы удалите элементы

Когда a List<T> заполняется, он удваивается по размеру, занимая в два раза больше памяти, но будет ли он автоматически уменьшаться в размере, если вы удалили из него элементы?

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

4b9b3361

Ответ 1

Нет, List никогда не уменьшает емкость, если вы явно не опустили ее самостоятельно, установив это свойство или используя TrimExcess, кроме случаев, когда вы вызываете Clear, и он может полностью удалить буфер.

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

Ответ 2

Нет, List<T> будет не автоматически возвращать любое из этого пространства, как это было в настоящее время реализовано, и вряд ли эта реализация изменится в ближайшее время.

Но это только часть истории. Помните, что, вообще говоря, List<T> хранит ссылки для ваших элементов. Если у вас есть большой список со многими объектами, и вы удаляете половину из них, так что ничего больше не использует удаленные объекты, что-то очень близкое к половине памяти, которую вы приписываете вашему списку будет, возвращаться, когда GC собирает эти объекты.

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

Наконец, есть способы вручную восстановить пространство... но использовать их экономно. Большую часть времени GC лучше знает. Вызов TrimExcess(), потому что вы просто удалили несколько элементов из большого списка, как правило, являются чистой потерей для вашего приложения.

Ответ 3

Из справочного источника видно, что метод Remove вызывает метод RemoveAt, который реализуется следующим образом:

public void RemoveAt(int index) {
    if ((uint)index >= (uint)_size) {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    Contract.EndContractBlock();
    _size--;
    if (index < _size) {
        Array.Copy(_items, index + 1, _items, index, _size - index);
    }
    _items[_size] = default(T);
    _version++;
}

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

В принципе, нет.

Также обратите внимание, что метод Clear не изменяет размер массива. Он вызывает Array.Clear, чтобы установить все элементы в базовом массиве в значения по умолчанию.

public void Clear() {
    if (_size > 0)
    {
        Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
        _size = 0;
    }
    _version++;
}