web-dev-qa-db-fra.com

QVector vs QList

J'ai une liste d'entiers que je dois parcourir mais un tableau est inadéquat. Quelles sont les différences entre vectors et lists et est-ce que je dois savoir avant de choisir un type?

Juste pour être clair, j'ai lu les documents QT mais c'est l'étendue de ce que je sais:

QList<T>, QLinkedList<T>, et QVector<T> fournit des fonctionnalités similaires. Voici un aperçu:

  • Dans la plupart des cas, QList est la bonne classe à utiliser. Son API basée sur un index est plus pratique que QLinkedList's API basée sur un itérateur, et elle est généralement plus rapide que QVector en raison de la façon dont elle stocke ses éléments en mémoire. Il se développe également en moins de code dans votre exécutable.
  • Si vous avez besoin d'une vraie liste chaînée, avec des garanties d'insertions à temps constant au milieu de la liste et des itérateurs aux éléments plutôt qu'aux index, utilisez QLinkedList.
  • Si vous souhaitez que les éléments occupent des positions de mémoire adjacentes, utilisez QVector.
67
jcuenod

QVector est principalement analogue à std::vector, comme vous pouvez le deviner d'après le nom. QList est plus proche de boost::ptr_deque, malgré l'association apparente avec std::list. Il ne stocke pas directement les objets, mais stocke à la place des pointeurs vers eux. Vous bénéficiez de tous les avantages des insertions rapides aux deux extrémités, et les réallocations impliquent de mélanger les pointeurs au lieu des constructeurs de copie, mais perdent la localité spatiale d'un _ std::deque ou std::vector, et gagnez beaucoup d'allocations de tas. Il a une certaine prise de décision pour éviter les allocations de tas pour les petits objets, retrouver la localité spatiale, mais d'après ce que je comprends, cela ne s'applique qu'aux choses plus petites qu'un int.

QLinkedList est analogue à std::list, et a tous ses inconvénients. D'une manière générale, cela devrait être votre dernier choix de conteneur.

La bibliothèque QT favorise fortement l'utilisation des objets QList, donc les privilégier dans votre propre code peut parfois éviter des ennuis inutiles. L'utilisation de tas supplémentaire et le positionnement aléatoire des données réelles peuvent théoriquement nuire dans certaines circonstances, mais sont souvent imperceptibles. Je suggère donc d'utiliser QList jusqu'à ce que le profilage suggère de passer à QVector. Si vous pensez que l'allocation contiguë sera importante [lire: vous vous connectez avec du code qui attend un T[] au lieu d'une QList<T>] qui peut aussi être une raison pour commencer avec QVector dès le départ.


Si vous posez des questions sur les conteneurs en général et que vous venez d'utiliser les documents QT comme référence, les informations ci-dessus sont moins utiles.

Un std::vector est un tableau que vous pouvez redimensionner. Tous les éléments sont stockés les uns à côté des autres et vous pouvez accéder rapidement aux éléments individuels. L'inconvénient est que les insertions ne sont efficaces qu'à une extrémité. Si vous placez quelque chose au milieu ou au début, vous devez copier les autres objets pour faire de la place. En notation big-oh, l'insertion à la fin est O (1), l'insertion ailleurs est O (N) et l'accès aléatoire est O (1).

Un std::deque est similaire, mais ne garantit pas que les objets sont stockés les uns à côté des autres, et permet que l'insertion aux deux extrémités soit O (1). Il nécessite également l'allocation de petits morceaux de mémoire à la fois, ce qui peut parfois être important. L'accès aléatoire est O(1) et l'insertion au milieu est O (N), comme pour un vector. La localité spatiale est pire que std::vector, mais les objets ont tendance à être regroupés de sorte que vous bénéficiez de certains avantages.

Un std::list est une liste chaînée. Il nécessite la plus grande surcharge de mémoire des trois conteneurs séquentiels standard, mais offre une insertion rapide n'importe où ... à condition de savoir à l'avance où vous devez insérer. Il n'offre pas d'accès aléatoire aux éléments individuels, vous devez donc itérer dans O (N). Mais une fois là, l'insertion réelle est O (1). Le plus grand avantage pour std::list est que vous pouvez les assembler rapidement ... si vous déplacez une plage entière de valeurs vers un autre std::list, toute l'opération est O (1). Il est également beaucoup plus difficile d'invalider des références dans la liste, ce qui peut parfois être important.

En règle générale, je préfère std::deque à std::vector, sauf si je dois pouvoir transmettre les données à une bibliothèque qui attend un tableau brut. std::vector est garanti contigu, donc &v[0] fonctionne à cet effet. Je ne me souviens pas de la dernière fois que j'ai utilisé un std::list, mais c'était presque certainement parce que j'avais besoin d'une garantie plus forte quant aux références restant valides.

114
Dennis Zickefoose

Les choses ont changé

Nous sommes maintenant dans Qt 5.8 et les choses ont changé, donc la documentation. Il donne une réponse claire et différente à cette question:

QVector devrait être votre premier choix par défaut. QVector<T> Donnera généralement de meilleures performances que QList<T>, Car QVector<T> Stocke toujours ses éléments séquentiellement en mémoire, où QList<T> Allouera ses éléments sur le tas, sauf si sizeof(T) <= sizeof(void*) et T a été déclaré soit Q_MOVABLE_TYPE soit Q_PRIMITIVE_TYPE en utilisant Q_DECLARE_TYPEINFO.

Voir les avantages et les inconvénients de l'utilisation de QList pour une explication. Cependant, QList est utilisé dans toutes les API Qt pour transmettre des paramètres et renvoyer des valeurs. Utilisez QList pour interfacer avec ces API.

42
dlewin

Dans QVector est similaire à std::vector. QLinkedList est similaire à std::list. QList est un vecteur basé sur un index, mais la position de la mémoire n'est pas garantie (comme std::deque).

11
Naszta

Du document QtList:

  • QList à utiliser dans la plupart des cas. Pour les structures avec un millier d'éléments, permet une insertion efficace au milieu et fournit un accès indexé. prepend() et append() très rapide car la mémoire est préallouée aux deux extrémités du tableau interne. QList<T> Est un tableau de pointeurs de type T. Si T a un pointeur ou un type de pointeur de type partagé Qt, l'objet est stocké directement dans le tableau

  • QVector à privilégier en cas de beaucoup de append() ou insert() de nouveaux éléments avec une taille supérieure à un pointeur puisque QVector alloue de la mémoire pour son éléments dans une seule allocation de segment de mémoire. Pour QList, l'insertion de l'ajout d'un nouvel élément nécessite l'allocation de mémoire du nouvel élément sur le tas. En bref, si vous voulez que les éléments occupent des positions de mémoire adjacentes, ou si vos éléments sont plus grands qu'un pointeur et que vous voulez éviter la surcharge de les allouer individuellement au tas au moment de l'insertion, utilisez QVector.

2
kiriloff