Имеет ли List.Insert какое-либо ограничение производительности?
Учитывая список:
List<object> SomeList = new List<object>();
Делает:
SomeList.Insert(i, val);
Vs.
SomeList.Add(val);
Имеет ли какое-либо ограничение производительности? Если это так, как это зависит от:
- i
- индекс вставки -
- SomeList.Count
- Размер списка
Ответы
Ответ 1
Класс List является общим эквивалентом класса ArrayList. Он реализует общий интерфейс IList, используя массив, размер которого динамически увеличивается по мере необходимости.
(источник)
Значение, что внутренние данные хранятся в виде массива, и поэтому вполне вероятно, что для выполнения insert
ему нужно будет переместить все элементы на место, поэтому его сложность - O (N), а add
является (амортизированным) постоянным временем O (1), поэтому да.
Резюме. Да, он будет почти всегда медленнее, и он будет медленнее, чем больше ваш список.
Ответ 2
Если сомневаетесь, проведите эмпирический эксперимент:
List<object> SomeList = new List<object>();
Stopwatch sw = new Stopwatch();
sw.Start();
for (var i = 0; i < 100000; i++)
SomeList.Insert(0, String.Empty);
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
sw.Reset();
SomeList = new List<object>();
sw.Start();
for (var i = 0; i < 100000; i++)
SomeList.Add(String.Empty);
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
Insert
занимает 2800 мс на моей машине; Add
занимает 0,8 мс. Так что да, Insert
гораздо менее производительный.
Ответ 3
Я понимаю, что на это уже был дан полный ответ, но я хотел бы указать, что эта информация легко доступна в документации MSDN.
В документации для List<T>.Insert()
указано:
Этот метод является операцией O (n), где n является Count.
В документации для List<T>.Add()
указано:
Если значение Count меньше емкости, этот метод является операцией O (1). Если емкость должна быть увеличена для размещения нового элемента, этот метод становится операцией O (n), где n является Count.
Если вам посчастливилось задавать этот вопрос, потому что у вас есть ситуация, когда вы хотите выполнять частые добавления в переднюю и заднюю части списка, то подходящей структурой данных для использования является Deque
.
Стивен Клири предоставил хорошую реализацию Deque здесь: http://nitodeque.codeplex.com/
Ответ 4
Моя первая мысль была "да, есть штраф за производительность, потому что Insert
нужно переместить все элементы в списке на один слот, чтобы освободить место для нового элемента", - но затем я внимательно прочитал вопрос. Итак:
В целом, Insert
занимает (возможно, много) больше времени, потому что ему нужно переместить все элементы, находящиеся в списке, чтобы освободить место для нового элемента, так что это O (n ) по длине списка (если список заполнен до емкости, ему также потребуется изменить размер, но это все еще операция O (n)). С другой стороны, Add
просто добавляет новый элемент без необходимости перемещать что-либо, чтобы он быстрее - операция O (1) (если только список не должен изменяться, как указано выше).
В этом конкретном примере, где список пуст для начала, разница в производительности не будет.
Конечно, все это отчасти спорно, потому что вы должны выбрать методы, основанные на том, что ваши намерения, если вы не имеете документированную проблему производительности.
Ответ 5
Для пустого списка это ничем не отличается.
Но для чего-либо еще он должен быть медленнее *, потому что для вставки спереди необходимо, чтобы весь массив поддержки был сдвинут один вправо.
* За исключением того, что Add
вызывает увеличение Capacity
.
Ответ 6
Конечно, да.
Так как List<T>
поддерживается массивом, любая операция для вставки в начале списка должна перемещать (или копировать) все остальные элементы массива. Операция, которая добавляется в конец списка, будет выполняться в постоянное время, независимо от количества элементов в списке.