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>
, etQVector<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 queQLinkedList's
API basée sur un itérateur, et elle est généralement plus rapide queQVector
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
.
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.
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 queQList<T
>, CarQVector<T>
Stocke toujours ses éléments séquentiellement en mémoire, oùQList<T>
Allouera ses éléments sur le tas, sauf sisizeof(T) <= sizeof(void*)
et T a été déclaré soitQ_MOVABLE_TYPE
soitQ_PRIMITIVE_TYPE
en utilisantQ_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. UtilisezQList
pour interfacer avec ces API.
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
).
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
.