Я рекомендую против std::forward_list
так же, как я рекомендую против std::list
практически во всех ситуациях. Лично я никогда не обнаружил ситуации в моем коде, где связанный список был лучшей структурой данных.
В C++ ваш сбор данных по умолчанию должен быть std::vector
. Это дает вам push_back
, если это то, что вам действительно нужно. Это технически не дает вам эффективного удаления и вставки из середины, если вы только посмотрите на абстрактные измерения сложности большой О из этой одной операции.В реальном мире, однако, std::vector
по-прежнему выигрывает даже для вставки и удаления в середине.
В качестве примера, Bjarne Stroustrup создал тест из 100 000 элементов std::list
против std::vector
. Он будет искать каждый элемент и удалять его. Затем он найдет точку вставки и вставит в середину. Он мог бы использовать двоичный поиск на std::vector
, но не сделал сравнение «более справедливым».
Результаты показывают сильную победу для std::vector
, даже в этой ситуации, когда std::list
должен быть сильным. Простое перемещение std::list
занимает намного больше времени, так как далеко друг от друга в памяти находятся все объекты. std::list
не относится к кэшу, что, возможно, является самым важным для современных процессоров.
The complete talk by Bjarne Stroustrup
Thorough explanation of the effects, with benchmarks at multiple sizes
Обратите внимание, что эта вторая ссылка здесь приведены некоторые ситуации, где вы, возможно, захотите использовать std::list
, например, когда размер элементов велик. Тем не менее, я был в ситуации, когда у меня много элементов в определенном порядке и нужно было удалить некоторые.
Эти элементы были больше, чем любой встроенный тип, но не огромный, возможно, 20-30 байтов каждый на 32-разрядном компьютере). Количество элементов было достаточно большим, так что вся моя структура данных составляла несколько сотен MiB. Сбор данных представлял собой набор значений, которые теоретически могли бы быть действительными на основе известной в настоящее время информации. Алгоритм повторяется по всем элементам и удаленным элементам, которые больше не могут быть действительными на основе новой информации, причем каждый проход, вероятно, удаляет около 80% оставшихся элементов.
Моя первая реализация была простой подход std::vector
, где я удалил недействительные элементы по мере прохождения. Это работало для небольших наборов тестовых данных, но когда я пытался делать настоящую вещь, было слишком медленно, чтобы быть полезным. Я переключился на std::list
в качестве контейнера, но использовал тот же алгоритм, и я видел значительные улучшения производительности. Однако было слишком медленно, чтобы быть полезным. Победившее изменение состояло в том, чтобы вернуться к std::vector
, но вместо удаления элементов, которые были плохими, я создал новый std::vector
, и любые найденные мной элементы были помещены в этот std::vector
, а затем в конце функции Я бы просто отказался от старого std::vector
и использовал новый, и это дало мне примерно такую же скорость по сравнению с std::list
, поскольку std::list
дал мне над моей оригинальной реализацией std::vector
, и это было достаточно быстро, чтобы быть полезным.
Обратите внимание, что двунаправленный 'std :: list' также поддерживает« быструю вставку и удаление элементов из любого места из контейнера »и имеет« push_back ». Стоимость - дополнительный указатель на запись. Является ли память настолько плотной, что вы не можете ее использовать? –
Зачем вам это нужно? Вы хотите развить свой список в обе стороны? Нельзя ли использовать 'push_front()' так же легко? –
Я хочу продолжить сортировку списка –