QVector vs QList

80

Tengo una lista de enteros sobre los que necesito iterar, pero una matriz es inadecuada. ¿Cuáles son las diferencias entre vectorsy? lists¿Hay algo que deba saber antes de elegir un tipo?

Para ser claros, he leído los documentos de QT, pero este es el alcance de lo que sé:

QList<T>, QLinkedList<T>y QVector<T>proporcionan una funcionalidad similar. Aquí hay una descripción general:

  • Para la mayoría de los propósitos, QListes la clase correcta para usar. Su API basada en índices es más conveniente que la QLinkedList'sAPI basada en iteradores y, por lo general, es más rápida que QVectordebido a la forma en que almacena sus elementos en la memoria. También se expande a menos código en su ejecutable.
  • Si necesita una lista enlazada real, con garantías de inserciones de tiempo constante en el medio de la lista e iteradores de elementos en lugar de índices, utilice QLinkedList.
  • Si desea que los elementos ocupen posiciones de memoria adyacentes, utilice QVector.
jcuenod
fuente

Respuestas:

122

QVectores mayormente análogo a std::vector, como se puede adivinar por el nombre. QListestá más cerca boost::ptr_deque, a pesar de la aparente asociación con std::list. No almacena objetos directamente, sino que almacena punteros a ellos. Obtiene todos los beneficios de las inserciones rápidas en ambos extremos, y las reasignaciones implican barajar punteros en lugar de constructores de copia, pero pierde la localidad espacial de un std::dequeo real std::vectory gana muchas asignaciones de montón. Tiene alguna toma de decisiones para evitar las asignaciones de montón para objetos pequeños, recuperando la localidad espacial, pero por lo que entiendo, solo se aplica a cosas más pequeñas que un int.

QLinkedListes análogo std::listy tiene todas sus desventajas. En términos generales, esta debería ser su última elección de contenedor.

La biblioteca QT favorece en gran medida el uso de QListobjetos, por lo que favorecerlos en su propio código a veces puede evitar un tedio innecesario. El uso adicional del montón y el posicionamiento aleatorio de los datos reales pueden, en teoría, dañar en algunas circunstancias, pero a menudo es imperceptible. Por lo tanto, sugeriría usar QListhasta que el perfil sugiera cambiar a un QVector. Si espera que la asignación contigua sea importante [lea: está interactuando con un código que espera un en T[]lugar de un QList<T>], eso también puede ser una razón para comenzar desde el principio QVector.


Si está preguntando sobre contenedores en general y solo utilizó los documentos de QT como referencia, entonces la información anterior es menos útil.

An std::vectores una matriz cuyo tamaño puede cambiar. Todos los elementos se almacenan uno al lado del otro y puede acceder a elementos individuales rápidamente. La desventaja es que las inserciones solo son eficientes en un extremo. Si pones algo en el medio, o al principio, tienes que copiar los otros objetos para hacer espacio. En notación de oh grande, la inserción al final es O (1), la inserción en cualquier otro lugar es O (N) y el acceso aleatorio es O (1).

An std::dequees similar, pero no garantiza que los objetos se almacenen uno al lado del otro y permite que la inserción en ambos extremos sea O (1). También requiere que se asignen porciones más pequeñas de memoria a la vez, lo que a veces puede ser importante. El acceso aleatorio es O (1) y la inserción en el medio es O (N), lo mismo que para a vector. La localidad espacial es peor que std::vector, pero los objetos tienden a agruparse, por lo que obtienes algunos beneficios.

An std::listes una lista vinculada. Requiere la mayor sobrecarga de memoria de los tres contenedores secuenciales estándar, pero ofrece una inserción rápida en cualquier lugar ... siempre que sepa de antemano dónde debe insertar. No ofrece acceso aleatorio a elementos individuales, por lo que debe iterar en O (N). Pero una vez allí, la inserción real es O (1). El mayor beneficio std::listes que puede unirlos rápidamente ... si mueve un rango completo de valores a un valor diferente std::list, toda la operación es O (1). También es mucho más difícil invalidar referencias en la lista, lo que a veces puede ser importante.

Como regla general, prefiero std::dequehacerlo std::vector, a menos que necesite poder pasar los datos a una biblioteca que espera una matriz sin procesar. std::vectorse garantiza contiguo, por lo que &v[0]funciona para este propósito. No recuerdo la última vez que usé un std::list, pero fue casi seguro porque necesitaba una garantía más fuerte para que las referencias siguieran siendo válidas.

Dennis Zickefoose
fuente
FWIW, std :: deque tiene bastante buenas garantías sobre referencias. Los iteradores se invalidan fácilmente, pero los punteros a los miembros son bastante robustos para las operaciones en la cola.
Ben
Gran respuesta. Pero tengo una pregunta adicional: ¿Cuál es más rápido de iterar? Tengo un conjunto de objetos, en realidad no se insertan o eliminan con frecuencia, pero necesito iterar sobre los objetos lo más rápido posible. ¿Cual es mas rápido?
birgersp
Buena informacion. Encontré la siguiente declaración más útil ... "La biblioteca QT favorece en gran medida el uso de objetos QList". En mi caso de uso, estoy tratando con un QTableWidget, al que le gustan QList y QListString. Por lo tanto, deje que su caso de uso dicte su decisión entre QVector y QList.
Panofish
1
¿Te acually como punto de referencia std::dequecontra std::vector? Te sorprenderá ...
Marc Mutz - mmutz
4
Tenga en cuenta que la documentación se ha actualizado a raíz de las quejas de los desarrolladores de Qt de que QList es una mala recomendación como contenedor predeterminado. Ahora dice: " QVector debería ser su primera opción predeterminada. [...] Sin embargo, QList se usa en todas las API de Qt para pasar parámetros y para devolver valores. Use QList para interactuar con esas API " . (Documentación de QList)
AntonyG
64

Las cosas han cambiado

Ahora estamos en Qt 5.8 y las cosas han cambiado, así que la documentación. Da una respuesta clara y diferente a esta pregunta:

QVectordebería ser su primera opción predeterminada. QVector<T>generalmente dará un mejor rendimiento que QList<T>, porque QVector<T>siempre almacena sus elementos secuencialmente en la memoria, donde QList<T>asignará sus elementos en el montón a menos que sizeof(T) <= sizeof(void*)y T haya sido declarado como un Q_MOVABLE_TYPEo un Q_PRIMITIVE_TYPEusing Q_DECLARE_TYPEINFO.

Consulte los pros y contras del uso QListpara obtener una explicación. Sin embargo, QListse utiliza en todas las API de Qt para pasar parámetros y devolver valores. Úselo QListpara interactuar con esas API.

dlewin
fuente
2
Esto debería subir y ahora se acepta como la respuesta.
ymoreau
12

En QVectores similar a std::vector. QLinkedListes similar a std::list. QListes un vector basado en índices, pero la posición de la memoria no está garantizada (como std::deque).

Naszta
fuente
3

Desde el documento QtList:

  • QList que se utilizará en la mayoría de los casos. Para estructuras con miles de elementos, permite una inserción eficiente en el medio y proporciona acceso indexado. prepend()y append()muy rápido, ya que la memoria está preasignada en ambos extremos de la matriz interna. QList<T>es una matriz de puntero de tipo T. Si T tiene un puntero o Qt tipo puntero de tipo compartido, el objeto se almacena directamente en la matriz

  • QVectorpreferible en el caso de muchos append()o insert()de elementos nuevos con un tamaño mayor que un puntero, ya que QVectorasigna memoria para sus elementos en una única asignación de montón. Para QList, insertar o agregar un nuevo elemento requiere la asignación de memoria del nuevo elemento en el montón. En resumen, si desea que los elementos ocupen posiciones de memoria adyacentes, o si sus elementos son más grandes que un puntero y desea evitar la sobrecarga de asignarlos en el montón individualmente en el momento de la inserción, utilice QVector.

Kiriloff
fuente
-2

QVector es como una matriz que puede cambiar de tamaño (aumentar o disminuir) pero conlleva transacciones, cálculos y tiempo pesados.

Por ejemplo, si desea agregar un elemento, se crea una nueva matriz, todos los elementos se copian en la nueva matriz, el nuevo elemento se agrega al final y la antigua matriz se elimina. Y viceversa para eliminar también.

Sin embargo, QLinkedListfunciona con punteros. Entonces, cuando se crea un nuevo elemento, solo se asigna un nuevo espacio de memoria y se vincula a la única parte de la memoria. Dado que funciona con punteros, es más rápido y eficiente.

Si tiene una lista de elementos cuyo tamaño no espera cambiar mucho, QVectorprobablemente sea bueno, pero generalmente QLinkedListse usa para la mayoría de los propósitos.

Mella
fuente
3
-1: QVector no viene con transacciones pesadas como afirma porque se asigna previamente y tiene una buena estrategia de crecimiento / reducción para agregar y hacer estallar. De hecho, anteponer / insertar requiere rangos de datos en movimiento, pero como son continuos en la memoria, esto se hace muy rápidamente (el caché puede funcionar bien con datos continuos, a diferencia de los fragmentos de montón dispersos como con QList). Su segunda afirmación de que QLinkedList se usa para la mayoría de los propósitos es completamente incorrecta. Se usa muy raramente. ¿Podría confundirlo con QList?
DerManu