J'ai remarqué dans Effective STL que
le vecteur est le type de séquence que devrait être utilisé par défaut.
Ça veut dire quoi? Il semble qu'ignorer l'efficacité vector
peut faire n'importe quoi.
Quelqu'un pourrait-il me proposer un scénario dans lequel vector
n'est pas une option réalisable mais list
doit être utilisé?
Situations dans lesquelles vous souhaitez insérer de nombreux éléments n'importe où, sauf la fin d'une séquence à plusieurs reprises.
Découvrez les garanties de complexité pour chaque type de conteneur:
Quelles sont les garanties de complexité des conteneurs standard?
vecteur:
liste:
En général, utilisez vector lorsque vous ne vous souciez pas du type de conteneur séquentiel que vous utilisez, mais si vous effectuez de nombreuses insertions ou effacements entre le conteneur et la fin de celui-ci, vous allez vouloir utiliser la liste. Ou si vous avez besoin d'un accès aléatoire, alors vous allez vouloir un vecteur, pas une liste. Autre que cela, il y a naturellement des cas où vous aurez besoin de l'un ou de l'autre en fonction de votre application, mais en général, ce sont de bonnes directives.
Si vous n'avez pas souvent besoin d'insérer des éléments, un vecteur sera plus efficace. Il possède une bien meilleure localisation de cache CPU qu'une liste. En d'autres termes, l'accès à un élément rend très probable que l'élément suivant soit présent dans le cache et puisse être récupéré sans avoir à lire une RAM lente.
La plupart des réponses ici manquent un détail important: à quoi sert-il?
Que voulez-vous conserver dans le conteneur?
S'il s'agit d'une collection de int
s, alors std::list
perdra dans tous les scénarios, que vous puissiez ou non réallouer, vous ne supprimez que du début, etc. Il serait extrêmement difficile de préparer un exemple où list<int>
bat vector<int>
. Et même dans ce cas, deque<int>
peut être meilleur ou plus proche, et ne consiste pas seulement à utiliser des listes, ce qui entraînera une surcharge de mémoire.
Cependant, si vous avez affaire à de gros blocs de données laids, et à quelques-uns d'entre eux, vous ne souhaitez pas sur-localisation lors de l'insertion, et la copie en raison d'une réaffectation serait un désastre. list<UglyBlob>
que vector<UglyBlob>
.
Cependant, si vous passez à vector<UglyBlob*>
ou même à vector<shared_ptr<UglyBlob> >
, la liste sera à la traîne.
Ainsi, le modèle d'accès, le nombre d'éléments cibles, etc. affectent toujours la comparaison, mais à mon avis - la taille des éléments - le coût de la copie, etc.
Une fonctionnalité spéciale de std :: list est le splicing (relier ou déplacer une partie ou une liste entière dans une liste différente).
Ou peut-être si votre contenu est très coûteux à copier. Dans un tel cas, il pourrait être moins coûteux, par exemple, de trier la collection avec une liste.
Notez également que si la collection est petite (et que le contenu n'est pas particulièrement coûteux à copier), un vecteur peut toujours surperformer une liste, même si vous insérez et effacez-le n'importe où. Une liste alloue chaque nœud individuellement, ce qui peut coûter beaucoup plus cher que de déplacer quelques objets simples.
Je ne pense pas qu'il y ait des règles très strictes. Cela dépend de ce que vous voulez faire avec le conteneur, ainsi que de la taille du conteneur et du type contenu. Un vecteur l'emporte généralement sur une liste, car il attribue son contenu sous la forme d'un seul bloc contigu (il s'agit essentiellement d'un tableau alloué de manière dynamique et, dans la plupart des cas, un tableau est le moyen le plus efficace de contenir un tas de choses).
Eh bien, les étudiants de ma classe semblent incapables de m'expliquer quand il est plus efficace d'utiliser des vecteurs, mais ils ont l'air assez heureux quand ils me conseillent d'utiliser des listes.
C'est comme ça que je le comprends
Listes : Chaque élément contient une adresse pour l'élément suivant ou précédent, de sorte qu'avec cette fonctionnalité, vous pouvez randomiser les éléments, même s'ils ne sont pas triés, l'ordre ne changera pas: c'est efficace si votre mémoire est fragmentée . Mais elle a aussi un autre très gros avantage: vous pouvez facilement insérer/supprimer des éléments, car la seule chose à faire est de changer quelques pointeurs . Inconvénient: Lire un élément aléatoire, vous devez passer d’un élément à l’autre jusqu’à ce que vous trouviez la bonne adresse.
Vectors : Lorsque vous utilisez des vecteurs, la mémoire est beaucoup plus organisée que les tableaux classiques: chaque nième élément est stocké juste après (n-1) élément et avant (n + 1) élément Pourquoi est-il meilleur que list? ?__. Parce qu'il permet un accès aléatoire rapide . Voici comment: si vous connaissez la taille d'un élément dans un vecteur, et s'ils sont contigus en mémoire, vous pouvez facilement prédire où se trouve le nième élément; vous n'avez pas besoin de parcourir tous les éléments d'une liste pour lire celui que vous voulez, avec un vecteur, vous le lisez directement, avec une liste que vous ne pouvez pas .. .. D'autre part, modifiez le tableau de vecteurs ou modifiez-le. une valeur est beaucoup plus lente.
Les listes sont plus appropriées pour garder la trace des objets qui peuvent être ajoutés/supprimés en mémoire . Les vecteurs sont plus appropriés lorsque vous souhaitez accéder à un élément à partir d'une grande quantité d'éléments uniques.
Je ne sais pas comment les listes sont optimisées, mais vous devez savoir que si vous voulez un accès rapide en lecture, vous devez utiliser des vecteurs, car la qualité de la STL pour raccourcir les listes ne sera pas aussi rapide en lecture-accès que vector.
Fondamentalement, un vecteur est un tableau avec gestion automatique de la mémoire. Les données sont contiguës en mémoire. Essayer d'insérer des données au milieu est une opération coûteuse.
Dans une liste, les données sont stockées dans des emplacements de mémoire non liés. L’insertion au milieu n’implique pas la copie de certaines données pour laisser de la place au nouveau.
Pour répondre plus précisément à votre question, je citerai cette page
les vecteurs sont généralement les plus efficaces dans le temps pour accéder aux éléments et pour ajouter ou supprimer des éléments à la fin de la séquence. Pour les opérations impliquant l’insertion ou le retrait d’éléments à des positions autres que la fin, leurs performances sont inférieures à celles de deques et de listes, et leurs itérateurs et références sont moins cohérents que les listes.
Chaque fois que vous ne pouvez pas faire invalider les itérateurs.
Lorsque vous avez beaucoup d'insertion ou de suppression au milieu de la séquence. par exemple. un gestionnaire de mémoire.
Préserver la validité des itérateurs est l'une des raisons d'utiliser une liste. Une autre possibilité est de ne pas vouloir réaffecter un vecteur lors de l’ajout d’éléments. Cela peut être géré par une utilisation intelligente de reserve (), mais dans certains cas, il pourrait être plus facile ou plus pratique de simplement utiliser une liste.
Lorsque vous souhaitez déplacer des objets entre des conteneurs, vous pouvez utiliser list::splice
.
Par exemple, un algorithme de partitionnement de graphes peut avoir un nombre constant d'objets divisés de manière récursive parmi un nombre croissant de conteneurs. Les objets doivent être initialisés une fois et rester toujours aux mêmes emplacements en mémoire. Il est beaucoup plus rapide de les réorganiser en les rapprochant qu'en les réaffectant.
Édition: Alors que les bibliothèques se préparent à implémenter C++ 0x, le cas général d’épissage d’une sous-séquence en une liste devient une complexité linéaire avec la longueur de la séquence. En effet, splice
(maintenant) doit parcourir la séquence pour compter le nombre d'éléments qu'il contient. (Parce que la liste doit enregistrer sa taille.) Compter et relier la liste est plus rapide que toute autre solution, et épisser une liste entière ou un seul élément est un cas particulier de complexité constante. Toutefois, si vous devez assembler de longues séquences, il vous faudra peut-être chercher un conteneur plus adapté, ancien et non conforme.
La seule règle stricte dans laquelle list
doit être utilisé est celle dans laquelle vous devez répartir les pointeurs sur les éléments du conteneur.
Contrairement à vector
, vous savez que la mémoire des éléments ne sera pas réallouée. Si tel est le cas, vous pourriez avoir des pointeurs sur la mémoire inutilisée, ce qui est au mieux un gros non-non et au pire une SEGFAULT
.
(Techniquement, une vector
de *_ptr
fonctionnerait aussi, mais dans ce cas, vous émulez list
, ce n'est donc qu'une sémantique.)
D'autres règles souples concernent les problèmes de performances possibles liés à l'insertion d'éléments au milieu d'un conteneur, list
étant préférable.