recomiendo contra std::forward_list
al igual que yo recomiendo en contra std::list
en casi todas las situaciones. Personalmente, nunca encontré una situación en mi código en la que una lista vinculada fuera la mejor estructura de datos.
En C++, su recopilación de datos predeterminada debe ser std::vector
. Le da eficiente push_back
, si eso es lo que realmente necesita. Desde el punto de vista técnico, no le ofrece una eliminación e inserción eficiente si solo observa las medidas abstractas de complejidad de gran O de esa operación.En el mundo real, sin embargo, std::vector
todavía gana incluso para insertar y eliminar en el medio.
Como ejemplo, Bjarne Stroustrup creó una prueba de un elemento std::list
de 100,000 frente a std::vector
. Buscaría cada uno de un elemento y lo eliminaría. Luego, encontraría un punto de inserción e insertaría en el centro. Podría haber usado una búsqueda binaria en el std::vector
, pero no hizo la comparación "más justa".
Los resultados muestran una victoria fuerte para std::vector
, incluso en esta situación donde se supone que std::list
es fuerte. Simplemente atravesando el std::list
toma mucho más tiempo debido a cuán separados están en la memoria todos los objetos. std::list
no es compatible con caché, que posiblemente sea lo más importante para los procesadores modernos.
The complete talk by Bjarne Stroustrup
Thorough explanation of the effects, with benchmarks at multiple sizes
Tenga en cuenta que este segundo enlace aquí da algunas situaciones de donde posiblemente puede que desee utilizar un std::list
, por ejemplo, cuando el tamaño de los elementos es grande. Sin embargo, he estado en una situación en la que tengo muchos elementos en un orden particular y necesito eliminar algunos.
Estos elementos eran más grandes que cualquier tipo incorporado, pero no grandes, quizás 20-30 bytes cada uno en una computadora de 32 bits). La cantidad de elementos era lo suficientemente grande para que toda mi estructura de datos tuviera unos cientos de MiB. La recopilación de datos fue un conjunto de valores que teóricamente podrían ser válidos según la información actualmente conocida. El algoritmo iteraba sobre todos los elementos y eliminaba los elementos que ya no podían ser válidos en función de la información nueva, con cada pase probablemente eliminando alrededor del 80% de los elementos restantes.
Mi primera implementación fue un enfoque directo std::vector
donde eliminé elementos no válidos a medida que atravesaba. Esto funcionó para pequeños conjuntos de datos de prueba, pero cuando traté de hacer algo real, fue demasiado lento para ser útil. Cambié a std::list
como contenedor, pero usé el mismo algoritmo y vi mejoras significativas en el rendimiento. Sin embargo, todavía era demasiado lento para ser útil. El cambio ganador fue volver a cambiar a std::vector
, pero en lugar de eliminar elementos que estaban mal, creé un nuevo std::vector
, y todos los elementos que encontré que eran buenos se pusieron en ese std::vector
, y luego al final de la función Simplemente descartaría el viejo std::vector
y usaría el nuevo, y esto me dio una aceleración tan grande como la std::list
como la me dio sobre la implementación original de std::vector
, y esto fue lo suficientemente rápido como para ser útil.
Tenga en cuenta que la 'std :: list' bidireccional también admite" inserción y eliminación rápida de elementos desde cualquier lugar del contenedor ", y tiene' push_back'. El costo es un puntero extra por entrada. ¿Es la memoria tan apretada que no puedes usar eso? –
¿Por qué lo necesitas? ¿Quieres hacer crecer tu lista en ambos sentidos? ¿No puedes usar 'push_front()' con la misma facilidad? –
Quiero seguir ordenando la lista –