TStringList, tableau dynamique ou liste liée dans Delphi?
J'ai un choix.
J'ai un certain nombre de déjà commandé des chaînes que j'ai besoin de stocker et d'accéder. Il semble que j'ai peut choisir entre l'utilisation de:
- Un TStringList
- Un Tableau Dynamique de chaînes, et
- Une Liste de chaînes de caractères (individuellement liée)
et Alan dans son commentaire m'a suggéré d'y ajouter également le choix:
TList<string>
Dans quelles circonstances est-uns de ces meilleurs que les autres?
Qui est le mieux pour les petites listes (de moins de 10 articles)?
Qui est le mieux pour les grandes listes (plus de 1000 points)?
Qui est le mieux pour les listes énormes (plus de 1 000 000 postes)?
Qui est le mieux pour minimiser l'utilisation de la mémoire?
Qui est le mieux pour minimiser les temps de chargement afin d'y ajouter des éléments sur la fin?
Qui est le mieux pour minimiser les temps d'accès pour accéder à l'ensemble de la liste de la première à la dernière?
Sur cette base (ou autres), des données dont la structure serait-elle préférable?
Pour référence, je suis à l'aide de Delphi 2009.
Dimitry dans un commentaire dit:
Décrire votre tâche et schéma d'accès aux données, il sera possible de vous donner une réponse exacte
D'accord. J'ai un logiciel de généalogie avec beaucoup de données.
Pour chaque personne que j'ai un certain nombre d'événements et d'attributs. Je suis le stockage comme de simples chaînes de texte, mais il ya beaucoup d'entre eux pour chaque personne, allant de 0 à quelques centaines. Et j'ai des milliers de personnes. Je n'ai pas besoin d'un accès aléatoire. J'ai seulement besoin d'eux associé un certain nombre de chaînes de caractères dans un ordre connu attaché à chaque personne. C'est mon cas, des milliers de "petites listes". Ils prennent le temps de chargement et l'utilisation de la mémoire, et de prendre le temps d'accès si j'en ai besoin (par exemple pour exporter l'intégralité de rapport généré).
Alors j'ai un peu plus grosses listes, par exemple, tous les noms des sections de mon "virtuel" treeview, ce qui peut avoir des centaines de milliers de noms. De nouveau j'ai seulement besoin d'une liste que je peut accès par index. Ceux-ci sont stockés séparément à partir de l'arborescence pour plus d'efficacité, et le treeview récupère seulement si nécessaire. Cela prend un certain temps à charger et il est très coûteux de la mémoire d'une montre pour mon programme. Mais je n'ai pas à vous soucier du temps d'accès, parce que quelques-uns sont accessibles à la fois.
J'espère que cela vous donne une idée de ce que je suis en train d'accomplir.
p.s. J'ai posté beaucoup de questions au sujet de l'optimisation de Delphi ici à StackOverflow. Mon programme lit 25 MO de fichiers avec 100 000 personnes et crée des structures de données et un rapport et des treeview pour eux dans les 8 secondes, mais utilise 175 MO de RAM pour le faire. Je suis en train de travailler à réduire, parce que je suis visant à charger les fichiers avec plusieurs millions de personnes dans 32-bit de Windows.
J'ai juste trouvé quelques excellentes suggestions pour optimiser une TList à ce StackOverflow question:
Est-il plus rapide TList mise en œuvre?
source d'informationauteur lkessler
Vous devez vous connecter pour publier un commentaire.
Sauf si vous avez des besoins particuliers, un
TStringList
est difficile à battre car il fournit leTStrings
de l'interface que de nombreux composants peuvent utiliser directement. AvecTStringList.Sorted := True
binaire de recherche seront utilisés, ce qui signifie que la recherche est très rapide. Vous bénéficiez également de mappage d'objets gratuitement, chaque élément peut également être associé à un pointeur, et vous obtenez toutes les méthodes existantes pour le triage, le flux des interfaces par des virgules, texte, texte délimité par, et ainsi de suite.D'autre part, pour des besoins particuliers, si vous avez besoin de faire beaucoup d'insertions et de suppressions, alors quelque chose de plus de l'approche d'une liste liée serait mieux. Mais ensuite, la recherche devient plus lent, et il est l'un des rares de la collection de chaînes en effet qui n'a jamais besoin de la recherche. Dans de telles situations, un certain type de hachage est souvent utilisée lorsqu'un hachage est créé à partir de, disons, les 2 premiers octets d'une chaîne de caractères (préallouer un tableau avec une longueur 65536, et les 2 premiers octets d'une chaîne est convertie directement dans un index de hachage à l'intérieur de cette gamme), puis à l'emplacement de hachage, une liste chaînée est stocké avec chaque élément clé comprenant les octets restants dans les cordes (pour économiser de l'espace---l'index de hachage contient déjà les deux premiers octets). Ensuite, le hachage initial de recherche est O(1), et la suite des insertions et des suppressions sont liés-liste-des-rapides. C'est un compromis qui peut être manipulé, et les leviers doivent être claires.
Un TStringList. Pros: a étendu la fonctionnalité, permettant de développer dynamiquement, de tri, d'enregistrer, de charger, de recherche, etc. Contre: sur la grande quantité de l'accès à ces éléments en fonction de l'index, Cordes[Index] est l'introduction sensible de la performance perdu (quelques pourcents), en les comparant à l'accès à un tableau, en charge de la mémoire pour chaque élément de la cellule.
Un Tableau Dynamique de chaînes de caractères. Pros: combine la capacité de développer dynamiquement, comme un TStrings, avec un accès rapide par l'index, le minimum de l'utilisation de la mémoire des autres. Inconvénients: limité standard "liste des chaînes" la fonctionnalité.
Une Liste de chaînes de caractères (individuellement liée). Avantages: la vitesse linéaire de l'ajout d'un élément dans la liste à la fin. Inconvénients: plus lent d'accès par l'index et de la recherche, limitée standard "liste des chaînes" la fonctionnalité, la charge de la mémoire pour "point suivant" pointeur, écart de surcharge pour chaque élément de l'allocation de la mémoire.
TList< string >. Comme ci-dessus.
TStringBuilder. Je ne possède pas une bonne idée, comment utiliser TStringBuilder comme un stockage de plusieurs chaînes de caractères.
En fait, il ya beaucoup plus d'approches:
La meilleure approche dépendra de la tâche.
Quelqu'un, peut-être même tableau statique avec le nombre total d'éléments variable nombre.
Pour les grandes listes je vais choisir:
- tableau dynamique, si j'ai besoin de beaucoup d'accès à l'index ou rechercher un élément
- table de hachage, si j'ai besoin d'une recherche par clé
- liste des tableaux dynamiques, si j'ai besoin de beaucoup d'élément ajoute pas d'accès par l'indice
tableau dynamique permettra de manger moins de mémoire. Mais la question n'est pas à propos de la surcharge, sujet sur lequel le nombre d'éléments de cette surcharge de devenir raisonnable. Et puis, comment gérer correctement ce nombre d'éléments.
tableau dynamique peut développer dynamiquement, mais d'un très grand nombre d'éléments, le gestionnaire de mémoire ne peuvent pas trouvé continu zone de mémoire. Bien qu'ils soient liés liste de travail jusqu'à ce qu'il y a une mémoire d'au moins une cellule, mais pour le coût de l'allocation de la mémoire pour chaque élément. L'approche mixte - liste des tableaux dynamiques devrait fonctionner.
tableau dynamique.
Pour quelle tâche ?
Si votre objectif est d'améliorer votre programme, au point qu'il peut charger les fichiers de généalogie avec des millions de personnes, alors décider entre les quatre structures de données dans votre question n'a pas vraiment l'intention de vous y rendre.
Faire les maths - vous êtes en train de chargement de 25 MO fichier avec environ 100000 personnes, ce qui provoque votre application de consommer 175 MO de mémoire. Si vous souhaitez charger des fichiers avec plusieurs millions de personnes en lui, vous pouvez estimer que, sans des changements drastiques à votre programme, vous devez multiplier vos besoins de mémoire par
n * 10
. Il n'y a aucun moyen de le faire que dans un processus 32 bits alors que de tout garder en mémoire la façon dont vous devez le faire.En gros, vous avez deux options:
De ne pas tout garder en mémoire à la fois, au lieu d'utiliser une base de données ou un fichier de la base de la solution qui vous charger des données à partir de quand vous en avez besoin. Je me souviens que tu avais d'autres questions à ce sujet déjà, et probablement décidé contre elle, donc je vais en rester là.
Tout garder en mémoire, mais dans la plupart de l'espace-efficace possible. Tant qu'il n'y a pas de 64 bits de Delphi cela devrait permettre quelques millions de personnes, selon la quantité de données, il y aura pour chaque personne. En recompilant ce pour le 64 bits va faire disparaître cette limite.
Si vous optez pour la deuxième option, alors vous devez réduire la consommation de mémoire de manière beaucoup plus agressive:
Utilisation chaîne de stage. Chaque chargés de l'élément de données dans votre programme qui contient les mêmes données, mais est contenue dans les différentes chaînes est fondamentalement mémoire gaspillée. Je comprends que votre programme est un observateur, pas un éditeur, de sorte que vous pouvez probablement vous en sortir avec seulement l'ajout de chaînes de votre piscine et de l'internement des chaînes. Faire de la chaîne de stage avec des millions de chaîne est toujours difficile, la "L'optimisation de la Consommation de Mémoire avec la Chaîne de Piscines" les billets de blog sur le SmartInspect blog peut vous donner quelques bonnes idées. Ces gars-là à faire régulièrement avec d'énormes fichiers de données et a eu à le faire fonctionner avec les mêmes contraintes que vous rencontrez.
Cela devrait aussi vous connecter cette réponse à votre question - si vous utilisez de la ficelle stage vous n'auriez pas besoin de garder des listes de chaînes de caractères dans vos structures de données, mais la liste de chaîne de piscine index.
Il peut également être avantageux d'utiliser plusieurs chaîne de piscines, comme pour les noms, mais un autre pour les endroits comme les villes ou les pays. Cela devrait accélérer l'insertion dans les piscines.
Utiliser le codage de la chaîne qui donne la plus petite représentation en mémoire. Le stockage de tout en tant que natif de Windows chaîne Unicode sera probablement consommer beaucoup plus d'espace que de stocker des chaînes de caractères en UTF-8, à moins que vous traitez régulièrement avec des chaînes de caractères qui contiennent principalement des personnages qui ont besoin de trois ou plusieurs octets en UTF-8.
En raison du caractère nécessaire de conversion de votre programme aura besoin de plus de cycles de PROCESSEUR pour afficher les chaînes de caractères, mais avec cette quantité de données, il est un digne compromis, comme l'accès à la mémoire sera le goulot d'étranglement, et la plus petite taille des données contribue à la diminution de l'accès à la mémoire de chargement.
Une question: Comment faites vous pour la requête: pensez-vous correspondre à la de chaînes ou de requête sur un ID ou la position dans la liste?
Meilleur pour les petits # chaînes:
Quel que soit le rend votre programme facile à comprendre. Programme de la lisibilité est très important et vous devriez le seul sacrifice dans la vraie points chauds dans votre application pour la vitesse.
Meilleur pour la mémoire (si c'est la plus grande contrainte) et les temps de chargement:
Garder toutes les chaînes de caractères dans un seul mémoire tampon (ou fichier mappé en mémoire) et de ne garder que des pointeurs vers des chaînes de caractères (ou de compensations). Chaque fois que vous besoin d'une chaîne vous pouvez découper une chaîne à l'aide de deux pointeurs et de le retourner comme un Delphi chaîne. De cette façon, vous éviter la surcharge de la structure de la chaîne elle-même (refcount, longueur int, codepage int et le gestionnaire de mémoire structures pour chaque chaîne de répartition.
Cela ne fonctionne bien que si les chaînes sont statiques et ne changent pas.
TList, TList<>, un tableau de chaîne de caractères et la solution ci-dessus ont une "liste", les frais généraux d'un pointeur par chaîne. Une liste liée a une surcharge d'au moins 2 pointeurs (simple liste chaînée) ou à 3 points (double liste chaînée). La liste liée solution n'a pas accès aléatoire rapide, mais qui permet un O(1) redimensionne où trhe les autres options ont O(lgN) (à l'aide d'un facteur de redimensionnement) ou O(N) à l'aide d'un fixe redimensionner.
Ce que je voudrais faire:
Si < 1000 articles et de la performance n'est pas la plus haute importance: l'utilisation TStringList ou une dyn la matrice de tout ce qui est plus facile pour vous.
sinon si statique: utiliser l'astuce ci-dessus. Cela vous donnera O(cgl) de la requête, moins de mémoire utilisée et très rapide temps de chargement (juste gulp ou utiliser un fichier mappé en mémoire)
Mentionnées toutes les structures de votre question échoue lors de l'utilisation de grandes quantités de données 1M+ des chaînes de caractères qui doit être dynamiquement chaned dans le code. À cette Époque, je voudrais utiliser une soldes arbre binaire ou une table de hachage en fonction du type de requêtes j'ai besoin de maken.
À partir de votre description, je ne suis pas entièrement sûr si elle pourrait convenir à votre conception, mais une façon de vous améliorer sur l'utilisation de la mémoire sans subir une énorme perte de performance est à l'aide d'un trie.
Alternative Possible:
J'ai découvert récemment SynBigTable (http://blog.synopse.info/post/2010/03/16/Synopse-Big-Table) qui a un TSynBigTableString classe pour stocker de grandes quantités de données à l'aide d'un index de chaîne.
Très simple, une seule couche de bigtable de mise en œuvre, et il utilise principalement disque de stockage, consomme beaucoup moins de mémoire que prévu lors de l'enregistrement de centaines de milliers d'enregistrements.
Aussi simple que:
de l'aide := UTF8String(Format('%s.%s', [nom, prénom]));
bigtable.Ajouter(de données, de l'aide)
et
bigtable.Obtenir de l'aide, données)
Un hic, les index doivent être uniques, et le coût de la mise à jour est un peu élevé (d'abord supprimer, puis insérez-la à nouveau)
TStringList
stocke un tableau de pointeur (string, TObject) de dossiers.TList
stocke un tableau de pointeurs.TStringBuilder
ne peut pas stocker un ensemble de cordes. Il est similaire .NET StringBuilder et ne doit être utilisé pour concaténer (beaucoup) les chaînes de caractères.Le redimensionnement dynamique de tableaux est lent, il ne faut pas la considérer comme une option.
Je voudrais utiliser Delphi générique
TList<string>
dans tous vos scénarios. Il stocke un tableau de chaînes de caractères (pas de chaîne de pointeurs). Il devrait avoir un accès plus rapide dans tous les cas en raison de l'absence (de l'onu)de la boxe.Vous pouvez être en mesure de trouver ou de mettre en œuvre un peu plus de liste liée solution si vous voulez seulement de l'accès séquentiel. Voir Delphi Algorithmes et Structures de Données.
Delphi fait la promotion de ses
TList
etTList<>
. Le tableau interne de la mise en œuvre est hautement optimisé et je n'ai jamais connu de la performance/des problèmes de mémoire lors de l'utilisation. Voir L'efficacité de TList et TStringList