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

Насколько дорогой список .RemoveAt(0) для общего списка?

С#,.NET4.

У нас есть критический код производительности, который вызывает некоторые проблемы. Это своего рода измененная очередь, которая на самом деле поддерживается списком. Мне было интересно, как дорого стоит элемент с индексом 0. Вопросы, которые приходят на ум, следующие:

  • В зависимости от того, как поддерживается резервная копия, выполняются ли какие-либо выделения/освобождения памяти после удаления в RemoveAt() для компенсации нового размера списка? Я знаю, например, что изменение размера массива может быть дорогостоящим (относительно говоря).
  • Я всегда представлял себе, что списки ведут себя как связанные списки, так что удаление элемента в нулевой позиции означает просто наличие ссылки на начало списка, скорректированную с предыдущего нулевого элемента на то, что раньше было первым (но теперь является первым элементом). Но мое "воображение" и реальность не всегда в строю.

Я всегда предполагал, что RemovedAt был O (1) для списков. Это тот случай?

4b9b3361

Ответ 1

List<T> поддерживается простым массивом плюс поле size, которое указывает, какая часть массива фактически используется. (для обеспечения будущего роста). Массив не изменяется, если вы не добавляете слишком много элементов или вызываете TrimExcess.

Remove - O(n), так как ему нужно сдвинуть остальную часть списка на один.


Вместо этого вы можете использовать LinkedList<T> (если вы не используете произвольный доступ) или написать собственный список, который отслеживает пустую часть спереди.

Ответ 2

Это O (n). Он даже говорит об этом MSDN.

Этот метод является операцией O (n), где n является (Count-index).

Ответ 3

Из того, что я могу сказать, это должна быть операция O (n). Внутри он использует Array.Copy (что является операцией O (n)), чтобы скопировать элементы после 0 обратно в массив поддержки списка. Из рефлектора:

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

Итак, для любого действительного индекса массива я он копирует все последующие элементы из я + 1 в i.