Ответ
Для сценария с частым добавлением элементов в конец оптимальным выбором будет std::vector<int>.
Причины:
- Амортизированная константная сложность O(1) для
push_back(). Хотя при исчерпании capacity происходит дорогостоящее перевыделение памяти и копирование, стратегия геометрического роста (обычно в 1.5-2 раза) гарантирует, что эти операции редки, а их стоимость «размазывается» по многим дешёвым добавлениям. - Отличная локальность данных. Элементы хранятся в непрерывном блоке памяти, что максимально эффективно для кэша процессора.
- Минимальные накладные расходы на хранение. Только данные и небольшой служебный overhead (указатели на начало, конец и конец выделенной памяти).
Критически важная оптимизация: Использование reserve() для предварительного выделения памяти, если примерный размер известен.
#include <vector>
#include <iostream>
#include <chrono>
int main() {
std::vector<int> data;
data.reserve(1'000'000); // Ключевая оптимизация: избегаем множественных перевыделений
for (int i = 0; i < 1'000'000; ++i) {
data.push_back(i); // Быстрое добавление
}
std::cout << "Size: " << data.size() << ", Capacity: " << data.capacity() << 'n';
return 0;
}
Альтернативы и когда их рассматривать:
std::deque: Подходит, если нужны частые вставки и в начало, и в конец (O(1)), но с чуть худшей локальностью, чем уvector.std::list/std::forward_list: Стоит выбрать только если нужны частые вставки/удаления в произвольных позициях (O(1) при наличии итератора), а не только в конце. Они имеют большое overhead на хранение указателей и плохую локальность.